summaryrefslogtreecommitdiffhomepage
path: root/android/src/test
diff options
context:
space:
mode:
authorAleksandr Granin <aleksandr@mullvad.net>2021-04-01 13:31:05 +0200
committerAleksandr Granin <aleksandr@mullvad.net>2021-04-01 13:31:05 +0200
commit563994ed9217e9d79b059a9da2410a6a68fe2169 (patch)
tree594f02a6eeb741a267ace7eb0970eabb92d467f9 /android/src/test
parenta487f5c04431013b884e1e5734370dc22e289279 (diff)
parent818a60da2ae1df45dc7d522bc244aeb9700e346e (diff)
downloadmullvadvpn-563994ed9217e9d79b059a9da2410a6a68fe2169.tar.xz
mullvadvpn-563994ed9217e9d79b059a9da2410a6a68fe2169.zip
Merge branch 'splittuneling-viewmodel'
Diffstat (limited to 'android/src/test')
-rw-r--r--android/src/test/kotlin/net/mullvad/mullvadvpn/TestCoroutineRule.kt24
-rw-r--r--android/src/test/kotlin/net/mullvad/mullvadvpn/TestUtils.kt11
-rw-r--r--android/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsIconManagerTest.kt2
-rw-r--r--android/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsProviderTest.kt8
-rw-r--r--android/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SplitTunnelingViewModelTest.kt225
5 files changed, 264 insertions, 6 deletions
diff --git a/android/src/test/kotlin/net/mullvad/mullvadvpn/TestCoroutineRule.kt b/android/src/test/kotlin/net/mullvad/mullvadvpn/TestCoroutineRule.kt
new file mode 100644
index 0000000000..1acdf9e577
--- /dev/null
+++ b/android/src/test/kotlin/net/mullvad/mullvadvpn/TestCoroutineRule.kt
@@ -0,0 +1,24 @@
+package net.mullvad.mullvadvpn
+
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.test.TestCoroutineDispatcher
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.setMain
+import org.junit.rules.TestWatcher
+import org.junit.runner.Description
+
+class TestCoroutineRule(
+ val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()
+) : TestWatcher() {
+
+ override fun starting(description: Description?) {
+ super.starting(description)
+ Dispatchers.setMain(testDispatcher)
+ }
+
+ override fun finished(description: Description?) {
+ super.finished(description)
+ Dispatchers.resetMain()
+ testDispatcher.cleanupTestCoroutines()
+ }
+}
diff --git a/android/src/test/kotlin/net/mullvad/mullvadvpn/TestUtils.kt b/android/src/test/kotlin/net/mullvad/mullvadvpn/TestUtils.kt
new file mode 100644
index 0000000000..4c4f043c06
--- /dev/null
+++ b/android/src/test/kotlin/net/mullvad/mullvadvpn/TestUtils.kt
@@ -0,0 +1,11 @@
+package net.mullvad.mullvadvpn
+
+import kotlin.test.assertTrue
+
+fun <T> assertLists(expected: List<T>, actual: List<T>, message: String? = null) = assertTrue(
+ expected.size == actual.size && expected.containsAll(actual) && actual.containsAll(expected),
+ message ?: """Expected list should have same size and contains same items.
+ | Expected(${expected.size}): $expected
+ | Actual(${actual.size}) : $actual
+ """.trimMargin()
+)
diff --git a/android/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsIconManagerTest.kt b/android/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsIconManagerTest.kt
index 6f871a28ee..e6d43621a1 100644
--- a/android/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsIconManagerTest.kt
+++ b/android/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsIconManagerTest.kt
@@ -86,7 +86,7 @@ class ApplicationsIconManagerTest {
}
@Test
- fun throw_exception_when_invoke_from_MainThread() {
+ fun test_throw_exception_when_invoke_from_MainThread() {
val testPackageName = "test"
every { mockedMainLooper.isCurrentThread } returns true
diff --git a/android/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsProviderTest.kt b/android/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsProviderTest.kt
index cb24158422..689047e565 100644
--- a/android/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsProviderTest.kt
+++ b/android/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsProviderTest.kt
@@ -7,6 +7,7 @@ import io.mockk.every
import io.mockk.mockk
import io.mockk.unmockkAll
import io.mockk.verifyAll
+import net.mullvad.mullvadvpn.assertLists
import org.junit.After
import org.junit.Test
@@ -42,11 +43,8 @@ class ApplicationsProviderTest {
val expected = listOf(
AppData(launchWithInternetPackageName, 0, launchWithInternetPackageName)
)
- assert(
- expected.size == result.size &&
- expected.containsAll(result) &&
- result.containsAll(expected)
- )
+
+ assertLists(expected, result)
verifyAll {
mockedPackageManager.getInstalledApplications(PackageManager.GET_META_DATA)
diff --git a/android/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SplitTunnelingViewModelTest.kt b/android/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SplitTunnelingViewModelTest.kt
new file mode 100644
index 0000000000..4bfd98f9da
--- /dev/null
+++ b/android/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SplitTunnelingViewModelTest.kt
@@ -0,0 +1,225 @@
+package net.mullvad.mullvadvpn.viewmodel
+
+import androidx.annotation.StringRes
+import androidx.lifecycle.viewModelScope
+import io.mockk.Runs
+import io.mockk.every
+import io.mockk.just
+import io.mockk.mockk
+import io.mockk.unmockkAll
+import io.mockk.verify
+import io.mockk.verifyAll
+import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.test.runBlockingTest
+import net.mullvad.mullvadvpn.R
+import net.mullvad.mullvadvpn.TestCoroutineRule
+import net.mullvad.mullvadvpn.applist.AppData
+import net.mullvad.mullvadvpn.applist.ApplicationsProvider
+import net.mullvad.mullvadvpn.applist.ViewIntent
+import net.mullvad.mullvadvpn.assertLists
+import net.mullvad.mullvadvpn.model.ListItemData
+import net.mullvad.mullvadvpn.model.WidgetState
+import net.mullvad.mullvadvpn.service.SplitTunneling
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.Timeout
+
+class SplitTunnelingViewModelTest {
+ @get:Rule
+ val testCoroutineRule = TestCoroutineRule()
+
+ @get:Rule
+ val timeout = Timeout(3000L, TimeUnit.MILLISECONDS)
+ private val mockedApplicationsProvider = mockk<ApplicationsProvider>()
+ private val mockedSplitTunneling = mockk<SplitTunneling>()
+ private lateinit var testSubject: SplitTunnelingViewModel
+
+ @Before
+ fun setup() {
+ every { mockedSplitTunneling.enabled } returns true
+ }
+
+ @After
+ fun tearDown() {
+ testSubject.viewModelScope.coroutineContext.cancel()
+ unmockkAll()
+ }
+
+ @Test
+ fun test_has_progress_on_start() = runBlockingTest(testCoroutineRule.testDispatcher) {
+ initTestSubject(emptyList())
+ val actualList: List<ListItemData> = testSubject.listItems.first()
+
+ val initialExpectedList = listOf(
+ createTextItem(R.string.split_tunneling_description),
+ createDivider(0),
+ createProgressItem()
+ )
+
+ assertLists(initialExpectedList, actualList)
+
+ verify(exactly = 1) {
+ mockedApplicationsProvider.getAppsList()
+ }
+ }
+
+ @Test
+ fun test_empty_app_list() = runBlockingTest(testCoroutineRule.testDispatcher) {
+ initTestSubject(emptyList())
+ testSubject.processIntent(ViewIntent.ViewIsReady)
+ val actualList = testSubject.listItems.first()
+ val expectedList = listOf(createTextItem(R.string.split_tunneling_description))
+ assertLists(expectedList, actualList)
+ }
+
+ @Test
+ fun test_apps_list_delivered() = runBlockingTest(testCoroutineRule.testDispatcher) {
+ val appExcluded = AppData("test.excluded", 0, "testName1")
+ val appNotExcluded = AppData("test.not.excluded", 0, "testName2")
+ every { mockedSplitTunneling.excludedAppList } returns listOf(appExcluded.packageName)
+
+ initTestSubject(listOf(appExcluded, appNotExcluded))
+ testSubject.processIntent(ViewIntent.ViewIsReady)
+
+ val actualList = testSubject.listItems.first()
+ val expectedList = listOf(
+ createTextItem(R.string.split_tunneling_description),
+ createDivider(0),
+ createMainItem(R.string.exclude_applications),
+ createApplicationItem(appExcluded, true),
+ createDivider(1),
+ createMainItem(R.string.all_applications),
+ createApplicationItem(appNotExcluded, false),
+ )
+
+ assertLists(expectedList, actualList)
+ verifyAll {
+ mockedSplitTunneling.enabled
+ mockedSplitTunneling.excludedAppList
+ }
+ }
+
+ @Test
+ fun test_remove_app_from_excluded() = runBlockingTest(testCoroutineRule.testDispatcher) {
+ val app = AppData("test", 0, "testName")
+ every { mockedSplitTunneling.excludedAppList } returns listOf(app.packageName)
+ every { mockedSplitTunneling.includeApp(app.packageName) } just Runs
+
+ initTestSubject(listOf(app))
+ testSubject.processIntent(ViewIntent.ViewIsReady)
+
+ val listBeforeAction = testSubject.listItems.first()
+ val expectedListBeforeAction = listOf(
+ createTextItem(R.string.split_tunneling_description),
+ createDivider(0),
+ createMainItem(R.string.exclude_applications),
+ createApplicationItem(app, true),
+ )
+
+ assertLists(expectedListBeforeAction, listBeforeAction)
+
+ val item = listBeforeAction.first { it.identifier == app.packageName }
+ testSubject.processIntent(ViewIntent.ChangeApplicationGroup(item))
+
+ val itemsAfterAction = testSubject.listItems.first()
+ val expectedList = listOf(
+ createTextItem(R.string.split_tunneling_description),
+ createDivider(1),
+ createMainItem(R.string.all_applications),
+ createApplicationItem(app, false),
+ )
+
+ assertLists(expectedList, itemsAfterAction)
+
+ verifyAll {
+ mockedSplitTunneling.enabled
+ mockedSplitTunneling.excludedAppList
+ mockedSplitTunneling.includeApp(app.packageName)
+ }
+ }
+
+ @Test
+ fun test_add_app_to_excluded() = runBlockingTest(testCoroutineRule.testDispatcher) {
+ val app = AppData("test", 0, "testName")
+ every { mockedSplitTunneling.excludedAppList } returns emptyList()
+ every { mockedSplitTunneling.excludeApp(app.packageName) } just Runs
+ initTestSubject(listOf(app))
+ testSubject.processIntent(ViewIntent.ViewIsReady)
+
+ val listBeforeAction = testSubject.listItems.first()
+ val expectedListBeforeAction = listOf(
+ createTextItem(R.string.split_tunneling_description),
+ createDivider(1),
+ createMainItem(R.string.all_applications),
+ createApplicationItem(app, false),
+ )
+
+ assertLists(expectedListBeforeAction, listBeforeAction)
+
+ val item = listBeforeAction.first { it.identifier == app.packageName }
+ testSubject.processIntent(ViewIntent.ChangeApplicationGroup(item))
+
+ val itemsAfterAction = testSubject.listItems.first()
+ val expectedList = listOf(
+ createTextItem(R.string.split_tunneling_description),
+ createDivider(0),
+ createMainItem(R.string.exclude_applications),
+ createApplicationItem(app, true),
+ )
+
+ assertLists(expectedList, itemsAfterAction)
+
+ verifyAll {
+ mockedSplitTunneling.enabled
+ mockedSplitTunneling.excludedAppList
+ mockedSplitTunneling.excludeApp(app.packageName)
+ }
+ }
+
+ private fun initTestSubject(appList: List<AppData>) {
+ every { mockedApplicationsProvider.getAppsList() } returns appList
+ testSubject = SplitTunnelingViewModel(
+ mockedApplicationsProvider,
+ mockedSplitTunneling,
+ testCoroutineRule.testDispatcher
+ )
+ }
+
+ private fun createApplicationItem(
+ appData: AppData,
+ checked: Boolean
+ ): ListItemData = ListItemData.build(appData.packageName) {
+ type = ListItemData.APPLICATION
+ text = appData.name
+ iconRes = appData.iconRes
+ action = ListItemData.ItemAction(appData.packageName)
+ widget = WidgetState.ImageState(
+ if (checked) R.drawable.ic_icons_remove else R.drawable.ic_icons_add
+ )
+ }
+
+ private fun createDivider(id: Int): ListItemData = ListItemData.build("space_$id") {
+ type = ListItemData.DIVIDER
+ }
+
+ private fun createMainItem(@StringRes text: Int): ListItemData =
+ ListItemData.build("header_$text") {
+ type = ListItemData.ACTION
+ textRes = text
+ }
+
+ private fun createTextItem(@StringRes text: Int): ListItemData =
+ ListItemData.build("text_$text") {
+ type = ListItemData.PLAIN
+ textRes = text
+ action = ListItemData.ItemAction(text.toString())
+ }
+
+ private fun createProgressItem(): ListItemData = ListItemData.build(identifier = "progress") {
+ type = ListItemData.PROGRESS
+ }
+}