diff --git a/README.md b/README.md
index ac48fc234d37a790b01b0e7c46855cd2aa1fb0f8..c9f95b9366df9e9931923063753ff3b539045e2b 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ App Lounge is an open-source application that allows you to install Android apps
App Lounge use the _Packaging by Features_ approach for packaging the code. A really good explanation for this approach can be found on Philip Hauer's [Package by Feature](https://web.archive.org/web/20211025104408/https://phauer.com/2020/package-by-feature/) blog post.
```
-.
+./root
├── api
│ ├── cleanapk
│ │ ├── blockedApps
diff --git a/app/src/main/java/foundation/e/apps/api/exodus/repositories/AppPrivacyInfoRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/api/exodus/repositories/AppPrivacyInfoRepositoryImpl.kt
index 0cfb3c5dfd7966e701d590b639962f3de9ead2a1..2fc815dadd4013c4f462a51e54d0b3f56dc948eb 100644
--- a/app/src/main/java/foundation/e/apps/api/exodus/repositories/AppPrivacyInfoRepositoryImpl.kt
+++ b/app/src/main/java/foundation/e/apps/api/exodus/repositories/AppPrivacyInfoRepositoryImpl.kt
@@ -1,3 +1,21 @@
+/*
+ * 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.exodus.repositories
import foundation.e.apps.api.Result
diff --git a/app/src/test/java/foundation/e/apps/exodus/AppPrivacyInfoRepositoryImplTest.kt b/app/src/test/java/foundation/e/apps/exodus/AppPrivacyInfoRepositoryImplTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a49f47a7cc44040d2ba307567e046c6cb7c07b6f
--- /dev/null
+++ b/app/src/test/java/foundation/e/apps/exodus/AppPrivacyInfoRepositoryImplTest.kt
@@ -0,0 +1,153 @@
+/*
+ * 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.exodus
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import foundation.e.apps.api.exodus.repositories.AppPrivacyInfoRepositoryImpl
+import foundation.e.apps.api.fused.data.FusedApp
+import foundation.e.apps.util.MainCoroutineRule
+import foundation.e.apps.utils.enums.Status
+import foundation.e.apps.utils.modules.CommonUtilsModule.LIST_OF_NULL
+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
+
+@ExperimentalCoroutinesApi
+class AppPrivacyInfoRepositoryImplTest {
+
+ // 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 fakeTrackerDao: FakeTrackerDao
+ private lateinit var fakeExodusTrackerApi: FakeExoudsTrackerApi
+ private lateinit var appPrivacyInfoRepository: AppPrivacyInfoRepositoryImpl
+
+ @Before
+ fun setup() {
+ fakeExodusTrackerApi = FakeExoudsTrackerApi()
+ fakeTrackerDao = FakeTrackerDao()
+ appPrivacyInfoRepository = AppPrivacyInfoRepositoryImpl(fakeExodusTrackerApi, fakeTrackerDao)
+ }
+
+ @Test
+ fun getAppPrivacyInfoWhenSuccess() = runTest {
+ val fusedApp = FusedApp(
+ _id = "113",
+ status = Status.UNAVAILABLE,
+ name = "Demo Three",
+ package_name = "foundation.e.demothree",
+ latest_version_code = 123,
+ is_pwa = true,
+ )
+ val result = appPrivacyInfoRepository.getAppPrivacyInfo(fusedApp, fusedApp.package_name)
+ assertEquals("getAppPrivacyInfo", true, result.isSuccess() )
+ assertEquals("getAppPrivacyInfo", 3, result.data?.trackerList?.size)
+ }
+
+ @Test
+ fun getAppPrivacyInfoWhenError() = runTest {
+ val fusedApp = FusedApp(
+ _id = "113",
+ status = Status.UNAVAILABLE,
+ name = "Demo Three",
+ package_name = "",
+ latest_version_code = 123,
+ is_pwa = true,
+ )
+ val result = appPrivacyInfoRepository.getAppPrivacyInfo(fusedApp, fusedApp.package_name)
+ assertEquals("getAppPrivacyInfo", false, result.isSuccess() )
+ }
+
+ @Test
+ fun getAppPrivacyInfoWhenTrackerDaoIsEmpty() = runTest {
+ val fusedApp = FusedApp(
+ _id = "113",
+ status = Status.UNAVAILABLE,
+ name = "Demo Three",
+ package_name = "a.b.c",
+ latest_version_code = 123,
+ is_pwa = true,
+ )
+ fakeTrackerDao.trackers.clear()
+ val result = appPrivacyInfoRepository.getAppPrivacyInfo(fusedApp, fusedApp.package_name)
+ assertEquals("getAppPrivacyInfo", 2, result.data?.trackerList?.size )
+ }
+
+ @Test
+ fun calculatePrivacyScoreWhenNoTrackers() {
+ val fusedApp = FusedApp(
+ _id = "113",
+ status = Status.UNAVAILABLE,
+ name = "Demo Three",
+ package_name = "a.b.c",
+ latest_version_code = 123,
+ is_pwa = true,
+ permsFromExodus = listOf(),
+ perms = listOf(),
+ trackers = listOf()
+ )
+ val privacyScore = appPrivacyInfoRepository.calculatePrivacyScore(fusedApp)
+ assertEquals("getAppPrivacyInfo", 10, privacyScore )
+ }
+
+ @Test
+ fun calculatePrivacyScoreWhenPermsAreNotAvailable() {
+ val fusedApp = FusedApp(
+ _id = "113",
+ status = Status.UNAVAILABLE,
+ name = "Demo Three",
+ package_name = "a.b.c",
+ latest_version_code = 123,
+ is_pwa = true,
+ perms = listOf(),
+ trackers = listOf()
+ )
+ val privacyScore = appPrivacyInfoRepository.calculatePrivacyScore(fusedApp)
+ assertEquals("getAppPrivacyInfo", -1, privacyScore )
+ }
+
+ @Test
+ fun calculatePrivacyScoreWhenTrackersAreNotAvailable() {
+ val fusedApp = FusedApp(
+ _id = "113",
+ status = Status.UNAVAILABLE,
+ name = "Demo Three",
+ package_name = "a.b.c",
+ latest_version_code = 123,
+ is_pwa = true,
+ permsFromExodus = listOf(),
+ perms = listOf(),
+ trackers = LIST_OF_NULL
+ )
+ val privacyScore = appPrivacyInfoRepository.calculatePrivacyScore(fusedApp)
+ assertEquals("getAppPrivacyInfo", 9, privacyScore )
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/java/foundation/e/apps/exodus/FakeExoudsTrackerApi.kt b/app/src/test/java/foundation/e/apps/exodus/FakeExoudsTrackerApi.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a668cd42fa37ebd15071bfd08db5fe11f2da2c6b
--- /dev/null
+++ b/app/src/test/java/foundation/e/apps/exodus/FakeExoudsTrackerApi.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.exodus
+
+import foundation.e.apps.api.exodus.ExodusTrackerApi
+import foundation.e.apps.api.exodus.Report
+import foundation.e.apps.api.exodus.Tracker
+import foundation.e.apps.api.exodus.Trackers
+import retrofit2.Response
+
+class FakeExoudsTrackerApi : ExodusTrackerApi {
+ private val trackers = mutableListOf(
+ Tracker(1, "Tracker A", "It;s tracer A", "", "", "", ""),
+ Tracker(2, "Tracker B", "It;s tracer B", "", "", "", ""),
+ Tracker(3, "Tracker C", "It;s tracer C", "", "", "", "")
+ )
+
+ override suspend fun getTrackerList(date: String): Response {
+ return Response.success(Trackers(mutableMapOf(Pair("one", trackers[0]), Pair("two", trackers[1]))))
+ }
+
+ override suspend fun getTrackerInfoOfApp(appHandle: String,
+ versionCode: Int): Response> {
+ if (appHandle.isEmpty()) {
+ return Response.error(404, null)
+ }
+ val reportOne = Report(System.currentTimeMillis(), "12-12-12", "1.2.3", "123", listOf(1,2,3), listOf())
+ val reportTwo = Report(System.currentTimeMillis(), "12-12-12", "1.2.3", "123", listOf(1,2,3), listOf())
+ return Response.success(listOf(reportOne, reportTwo))
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/java/foundation/e/apps/exodus/FakeTrackerDao.kt b/app/src/test/java/foundation/e/apps/exodus/FakeTrackerDao.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c2de7f16ab994465c80c77a24360ec40eb4164b0
--- /dev/null
+++ b/app/src/test/java/foundation/e/apps/exodus/FakeTrackerDao.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.exodus
+
+import foundation.e.apps.api.exodus.Tracker
+import foundation.e.apps.api.exodus.TrackerDao
+
+class FakeTrackerDao : TrackerDao {
+
+ val trackers = mutableListOf(
+ Tracker(1, "Tracker A", "It;s tracer A", "", "", "", ""),
+ Tracker(2, "Tracker B", "It;s tracer B", "", "", "", ""),
+ Tracker(3, "Tracker C", "It;s tracer C", "", "", "", "")
+ )
+
+ override suspend fun saveTrackers(trackerList: List): List {
+ trackers.addAll(trackerList)
+ return trackerList.map { it.id }
+ }
+
+ override suspend fun getTrackers(): List {
+ return trackers
+ }
+}
\ No newline at end of file