diff options
| author | Aleksandr Granin <aleksandr@mullvad.net> | 2021-04-01 13:31:05 +0200 |
|---|---|---|
| committer | Aleksandr Granin <aleksandr@mullvad.net> | 2021-04-01 13:31:05 +0200 |
| commit | 563994ed9217e9d79b059a9da2410a6a68fe2169 (patch) | |
| tree | 594f02a6eeb741a267ace7eb0970eabb92d467f9 /android/src/test | |
| parent | a487f5c04431013b884e1e5734370dc22e289279 (diff) | |
| parent | 818a60da2ae1df45dc7d522bc244aeb9700e346e (diff) | |
| download | mullvadvpn-563994ed9217e9d79b059a9da2410a6a68fe2169.tar.xz mullvadvpn-563994ed9217e9d79b059a9da2410a6a68fe2169.zip | |
Merge branch 'splittuneling-viewmodel'
Diffstat (limited to 'android/src/test')
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 + } +} |
