From 0995760e5ea6dde06b56065ea45346c1f9672839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20G=C3=B6ransson?= Date: Fri, 29 Sep 2023 20:10:22 +0200 Subject: [PATCH 1/9] Add extension for projectScope --- .../mullvadvpn/test/arch/extensions/KonsistExtensions.kt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/extensions/KonsistExtensions.kt diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/extensions/KonsistExtensions.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/extensions/KonsistExtensions.kt new file mode 100644 index 000000000000..b228575b467c --- /dev/null +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/extensions/KonsistExtensions.kt @@ -0,0 +1,5 @@ +package net.mullvad.mullvadvpn.test.arch.extensions + +import com.lemonappdev.konsist.api.Konsist + +fun projectScope() = Konsist.scopeFromProject() From 3d38b605ca87c63e293afecb3e37f062c4f31265 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20G=C3=B6ransson?= Date: Fri, 29 Sep 2023 20:02:08 +0200 Subject: [PATCH 2/9] Add Compose Preview test and fix issues --- .../compose/button/ConnectionButton.kt | 2 +- .../compose/button/SwitchLocationButton.kt | 2 +- .../mullvadvpn/compose/cell/CustomPortCell.kt | 2 +- .../compose/component/ConnectionStatusText.kt | 2 +- .../mullvadvpn/compose/component/List.kt | 2 +- .../compose/component/LocationInfo.kt | 2 +- .../compose/component/NotificationBanner.kt | 2 +- .../mullvadvpn/compose/component/Scrollbar.kt | 8 +++---- .../compose/dialog/CustomPortDialog.kt | 2 +- .../compose/screen/ConnectScreen.kt | 2 +- .../compose/screen/DeviceListScreen.kt | 2 +- .../compose/screen/DeviceRevokedScreen.kt | 2 +- .../compose/screen/PrivacyDisclaimerScreen.kt | 2 +- .../compose/screen/SelectLocationScreen.kt | 2 +- .../compose/screen/SplitTunnelingScreen.kt | 2 +- .../buildSrc/src/main/kotlin/Dependencies.kt | 3 +++ android/test/arch/build.gradle.kts | 15 ++++--------- .../test/arch/compose/ComposePreviewTests.kt | 22 +++++++++++++++++++ 18 files changed, 47 insertions(+), 29 deletions(-) create mode 100644 android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/compose/ComposePreviewTests.kt diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/ConnectionButton.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/ConnectionButton.kt index 78521592478f..18eebb8b85ad 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/ConnectionButton.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/ConnectionButton.kt @@ -100,7 +100,7 @@ fun ConnectionButton( @Preview @Composable -fun PreviewConnectionButton() { +private fun PreviewConnectionButton() { AppTheme { ConnectionButton( text = "Disconnect", diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/SwitchLocationButton.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/SwitchLocationButton.kt index e58e7e22025e..906afbb65ce6 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/SwitchLocationButton.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/button/SwitchLocationButton.kt @@ -22,7 +22,7 @@ import net.mullvad.mullvadvpn.lib.theme.Dimens @Preview @Composable -fun PreviewSwitchLocationButton() { +private fun PreviewSwitchLocationButton() { AppTheme { SpacedColumn { SwitchLocationButton(onClick = {}, text = "Switch Location", showChevron = false) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/CustomPortCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/CustomPortCell.kt index ab3e45c5c4e8..7021081963b8 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/CustomPortCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/CustomPortCell.kt @@ -33,7 +33,7 @@ import net.mullvad.mullvadvpn.lib.theme.Dimens @Preview @Composable -fun PreviewCustomPortCell() { +private fun PreviewCustomPortCell() { AppTheme { SpacedColumn(Modifier.background(MaterialTheme.colorScheme.background)) { CustomPortCell(title = "Title", isSelected = true, port = "444") diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/ConnectionStatusText.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/ConnectionStatusText.kt index fa896b2c9afe..742302ce91ae 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/ConnectionStatusText.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/ConnectionStatusText.kt @@ -15,7 +15,7 @@ import net.mullvad.talpid.tunnel.ErrorStateCause @Preview @Composable -fun PreviewConnectionStatusText() { +private fun PreviewConnectionStatusText() { AppTheme { SpacedColumn { ConnectionStatusText(TunnelState.Disconnected) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/List.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/List.kt index 7ff5b7f6cfab..66d9aea0fa35 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/List.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/List.kt @@ -32,7 +32,7 @@ import net.mullvad.mullvadvpn.lib.theme.typeface.listItemText @Preview @Composable -fun PreviewListItem() { +private fun PreviewListItem() { AppTheme { Column { ListItem(text = "No subtext No icon not loading", isLoading = false, onClick = {}) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/LocationInfo.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/LocationInfo.kt index 3f12457b8692..161bce9b7583 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/LocationInfo.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/LocationInfo.kt @@ -25,7 +25,7 @@ import net.mullvad.talpid.net.TransportProtocol @Preview @Composable -fun PreviewLocationInfo() { +private fun PreviewLocationInfo() { AppTheme { LocationInfo( onToggleTunnelInfo = {}, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/NotificationBanner.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/NotificationBanner.kt index 4e638d56b2e7..0f7fa7411719 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/NotificationBanner.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/NotificationBanner.kt @@ -40,7 +40,7 @@ import org.joda.time.DateTime @Preview @Composable -fun PreviewNotificationBanner() { +private fun PreviewNotificationBanner() { AppTheme { SpacedColumn(Modifier.background(color = MaterialTheme.colorScheme.background)) { val versionInfoNotification = diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scrollbar.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scrollbar.kt index 4af39cf4b5ae..35ddc585923f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scrollbar.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scrollbar.kt @@ -287,7 +287,7 @@ private val FadeOutAnimationSpec = @Preview(widthDp = 400, heightDp = 400, showBackground = true) @Composable -internal fun ScrollbarPreview() { +private fun PreviewScrollbar() { val state = rememberScrollState() Column( modifier = Modifier.drawVerticalScrollbar(state).verticalScroll(state), @@ -300,7 +300,7 @@ internal fun ScrollbarPreview() { @Preview(widthDp = 400, heightDp = 400, showBackground = true) @Composable -internal fun LazyListScrollbarPreview() { +private fun PreviewLazyListScrollbar() { val state = rememberLazyListState() LazyColumn(modifier = Modifier.drawVerticalScrollbar(state), state = state) { items(50) { @@ -311,7 +311,7 @@ internal fun LazyListScrollbarPreview() { @Preview(widthDp = 400, showBackground = true) @Composable -internal fun HorizontalScrollbarPreview() { +private fun PreviewHorizontalScrollbar() { val state = rememberScrollState() Row(modifier = Modifier.drawHorizontalScrollbar(state).horizontalScroll(state)) { repeat(50) { @@ -325,7 +325,7 @@ internal fun HorizontalScrollbarPreview() { @Preview(widthDp = 400, showBackground = true) @Composable -internal fun LazyListHorizontalScrollbarPreview() { +private fun PreviewLazyListHorizontalScrollbar() { val state = rememberLazyListState() LazyRow(modifier = Modifier.drawHorizontalScrollbar(state), state = state) { items(50) { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CustomPortDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CustomPortDialog.kt index 3c36ca8f8885..e23af1356484 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CustomPortDialog.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/CustomPortDialog.kt @@ -30,7 +30,7 @@ import net.mullvad.mullvadvpn.util.isPortInValidRanges @Preview @Composable -fun PreviewCustomPortDialog() { +private fun PreviewCustomPortDialog() { AppTheme { CustomPortDialog( onSave = {}, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt index 8ca98c410a9d..f2d5f9cd9552 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ConnectScreen.kt @@ -55,7 +55,7 @@ private const val CONNECT_BUTTON_THROTTLE_MILLIS = 1000 @Preview @Composable -fun PreviewConnectScreen() { +private fun PreviewConnectScreen() { val state = ConnectUiState.INITIAL AppTheme { ConnectScreen( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt index 2e72e1280147..37669a98512c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt @@ -41,7 +41,7 @@ import net.mullvad.mullvadvpn.util.formatDate @Composable @Preview -fun PreviewDeviceListScreen() { +private fun PreviewDeviceListScreen() { AppTheme { DeviceListScreen( state = diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreen.kt index c7f0c8416f96..22fa8a0469a8 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreen.kt @@ -32,7 +32,7 @@ import net.mullvad.mullvadvpn.lib.theme.AppTheme @Preview @Composable -fun PreivewDeviceRevokedScreen() { +private fun PreviewDeviceRevokedScreen() { AppTheme { DeviceRevokedScreen(state = DeviceRevokedUiState.SECURED) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/PrivacyDisclaimerScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/PrivacyDisclaimerScreen.kt index 50ec92c45e5a..3de32531e898 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/PrivacyDisclaimerScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/PrivacyDisclaimerScreen.kt @@ -36,7 +36,7 @@ import net.mullvad.mullvadvpn.lib.theme.AppTheme @Preview @Composable -fun PreviewPrivacyDisclaimerScreen() { +private fun PreviewPrivacyDisclaimerScreen() { AppTheme { PrivacyDisclaimerScreen({}, {}) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt index 3376ffe422e6..b7f23ea76849 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt @@ -54,7 +54,7 @@ import net.mullvad.mullvadvpn.relaylist.RelayItem @Preview @Composable -fun PreviewSelectLocationScreen() { +private fun PreviewSelectLocationScreen() { val state = SelectLocationUiState.ShowData( countries = listOf(RelayCountry("Country 1", "Code 1", false, emptyList())), diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreen.kt index f5e512f8f699..ad9a7d3af26f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreen.kt @@ -44,7 +44,7 @@ import net.mullvad.mullvadvpn.lib.theme.Dimens @Preview @Composable -fun PreviewSplitTunnelingScreen() { +private fun PreviewSplitTunnelingScreen() { AppTheme { SplitTunnelingScreen( uiState = diff --git a/android/buildSrc/src/main/kotlin/Dependencies.kt b/android/buildSrc/src/main/kotlin/Dependencies.kt index 80c33136835d..d23399c915f6 100644 --- a/android/buildSrc/src/main/kotlin/Dependencies.kt +++ b/android/buildSrc/src/main/kotlin/Dependencies.kt @@ -62,6 +62,9 @@ object Dependencies { const val uiTooling = "androidx.compose.ui:ui-tooling:${Versions.Compose.base}" const val uiToolingPreview = "androidx.compose.ui:ui-tooling-preview:${Versions.Compose.base}" + const val uiToolingAndroidPreview = + "androidx.compose.ui:ui-tooling-preview-android:${Versions.Compose.base}" + } object Koin { diff --git a/android/test/arch/build.gradle.kts b/android/test/arch/build.gradle.kts index 0a28fd2fab3c..dff039f0e2f8 100644 --- a/android/test/arch/build.gradle.kts +++ b/android/test/arch/build.gradle.kts @@ -7,18 +7,14 @@ android { namespace = "net.mullvad.mullvadvpn.test.arch" compileSdk = Versions.Android.compileSdkVersion - defaultConfig { - minSdk = Versions.Android.minSdkVersion - } + defaultConfig { minSdk = Versions.Android.minSdkVersion } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = Versions.jvmTarget - } + kotlinOptions { jvmTarget = Versions.jvmTarget } lint { lintConfig = file("${rootProject.projectDir}/config/lint.xml") @@ -28,14 +24,11 @@ android { } androidComponents { - beforeVariants { variantBuilder -> - variantBuilder.apply { - enable = name != "release" - } - } + beforeVariants { variantBuilder -> variantBuilder.apply { enable = name != "release" } } } dependencies { + testImplementation(Dependencies.Compose.uiToolingAndroidPreview) testImplementation(Dependencies.AndroidX.appcompat) testImplementation(Dependencies.junit) testImplementation(Dependencies.konsist) diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/compose/ComposePreviewTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/compose/ComposePreviewTests.kt new file mode 100644 index 000000000000..64b221d0c073 --- /dev/null +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/compose/ComposePreviewTests.kt @@ -0,0 +1,22 @@ +package net.mullvad.mullvadvpn.test.arch.compose + +import androidx.compose.ui.tooling.preview.Preview +import com.lemonappdev.konsist.api.Konsist +import com.lemonappdev.konsist.api.ext.list.withAllAnnotationsOf +import com.lemonappdev.konsist.api.verify.assert +import org.junit.Test + +class ComposePreviewTests { + @Test + fun `all preview functions are private`() { + allPreviewFunctions().assert { it.hasPrivateModifier } + } + + @Test + fun `all preview functions are prefixed with Preview`() { + allPreviewFunctions().assert { it.name.startsWith("Preview") } + } + + private fun allPreviewFunctions() = + Konsist.scopeFromProduction().functions().withAllAnnotationsOf(Preview::class) +} From 0d16ebb525f2d33b257fa2dd6da078a56b585a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20G=C3=B6ransson?= Date: Fri, 29 Sep 2023 20:06:36 +0200 Subject: [PATCH 3/9] Add general Konsist tests --- .../mullvadvpn/test/arch/GeneralTests.kt | 27 +++++++++++++++++++ .../mullvadvpn/test/mockapi/util/JsonUtils.kt | 3 +-- 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/GeneralTests.kt diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/GeneralTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/GeneralTests.kt new file mode 100644 index 000000000000..a40f93c5f52a --- /dev/null +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/GeneralTests.kt @@ -0,0 +1,27 @@ +package net.mullvad.mullvadvpn.test.arch + +import com.lemonappdev.konsist.api.Konsist +import com.lemonappdev.konsist.api.ext.list.properties +import com.lemonappdev.konsist.api.verify.assert +import com.lemonappdev.konsist.api.verify.assertNot +import org.junit.Test + +class GeneralTests { + @Test + fun `package name must match file path`() { + Konsist.scopeFromProject().packages.assert { it.hasMatchingPath } + } + + @Test + fun `no field should have 'm' prefix`() { + Konsist.scopeFromProject().classes().properties().assertNot { + val secondCharacterIsUppercase = it.name.getOrNull(1)?.isUpperCase() ?: false + it.name.startsWith('m') && secondCharacterIsUppercase + } + } + + @Test + fun `no empty files allowed`() { + Konsist.scopeFromProject().files.assertNot { it.text.isEmpty() } + } +} diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/util/JsonUtils.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/util/JsonUtils.kt index 62320a07e6ff..2ccae9499e67 100644 --- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/util/JsonUtils.kt +++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/util/JsonUtils.kt @@ -1,6 +1,5 @@ -package net.mullvad.mullvadvpn.test.mockapi +package net.mullvad.mullvadvpn.test.mockapi.util -import net.mullvad.mullvadvpn.test.mockapi.util.formatStrictlyAccordingToIso8601AndRfc3339 import org.joda.time.DateTime import org.json.JSONObject From e51bb93ed06366a9922700e7bb3c8129b5974a31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20G=C3=B6ransson?= Date: Fri, 29 Sep 2023 20:06:50 +0200 Subject: [PATCH 4/9] Add architecture tests --- .../mullvadvpn/test/arch/ArchitectureTests.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ArchitectureTests.kt diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ArchitectureTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ArchitectureTests.kt new file mode 100644 index 000000000000..ecb2fa18bb1d --- /dev/null +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ArchitectureTests.kt @@ -0,0 +1,18 @@ +package net.mullvad.mullvadvpn.test.arch + +import com.lemonappdev.konsist.api.Konsist +import com.lemonappdev.konsist.api.architecture.KoArchitectureCreator.assertArchitecture +import com.lemonappdev.konsist.api.architecture.Layer +import org.junit.Test + +class ArchitectureTests { + + @Test + fun `domain layer depends on nothing`() { + Konsist.scopeFromProduction().assertArchitecture { + val domain = Layer("Domain", "net.mullvad.mullvadvpn.model..") + + domain.dependsOnNothing() + } + } +} From 5052cdea50ce6d00a444dfd4c3811cf1dadaeb25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20G=C3=B6ransson?= Date: Fri, 29 Sep 2023 20:09:39 +0200 Subject: [PATCH 5/9] Add class test and fix issues --- .../mullvad/mullvadvpn/model/TunnelState.kt | 42 +++++++++--------- .../mullvadvpn/service/MullvadVpnService.kt | 15 ++++--- .../service/endpoint/AccountCache.kt | 19 ++++---- .../service/endpoint/AuthTokenCache.kt | 12 ++--- .../service/endpoint/LocationInfoCache.kt | 13 +++--- .../service/endpoint/RelayListListener.kt | 13 +++--- .../service/endpoint/ServiceEndpoint.kt | 15 ++++--- .../AccountExpiryNotification.kt | 11 ++--- .../notifications/TunnelStateNotification.kt | 8 ++-- .../TunnelStateNotificationAction.kt | 44 +++++++++---------- .../test/arch/classes/ClassTests.kt | 21 +++++++++ 11 files changed, 120 insertions(+), 93 deletions(-) create mode 100644 android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/ClassTests.kt diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt index c3d58d2ca7e9..364c8861ceda 100644 --- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt @@ -34,6 +34,27 @@ sealed class TunnelState : Parcelable { } } + override fun toString(): String = + when (this) { + is Disconnected -> DISCONNECTED + is Connecting -> CONNECTING + is Connected -> CONNECTED + is Disconnecting -> { + if (actionAfterDisconnect == ActionAfterDisconnect.Reconnect) { + RECONNECTING + } else { + DISCONNECTING + } + } + is Error -> { + if (errorState.isBlocking) { + BLOCKING + } else { + ERROR + } + } + } + companion object { const val DISCONNECTED = "disconnected" const val CONNECTING = "connecting" @@ -58,25 +79,4 @@ sealed class TunnelState : Parcelable { } } } - - override fun toString(): String = - when (this) { - is Disconnected -> DISCONNECTED - is Connecting -> CONNECTING - is Connected -> CONNECTED - is Disconnecting -> { - if (actionAfterDisconnect == ActionAfterDisconnect.Reconnect) { - RECONNECTING - } else { - DISCONNECTING - } - } - is Error -> { - if (errorState.isBlocking) { - BLOCKING - } else { - ERROR - } - } - } } diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt index 3f7149b282a4..cd05d5cc7139 100644 --- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt +++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt @@ -28,13 +28,6 @@ import net.mullvad.talpid.TalpidVpnService import org.koin.core.context.loadKoinModules class MullvadVpnService : TalpidVpnService() { - companion object { - private val TAG = "mullvad" - - init { - System.loadLibrary("mullvad_jni") - } - } private enum class PendingAction { Connect, @@ -273,4 +266,12 @@ class MullvadVpnService : TalpidVpnService() { startActivity(intent) } + + companion object { + private val TAG = "mullvad" + + init { + System.loadLibrary("mullvad_jni") + } + } } diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AccountCache.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AccountCache.kt index d6ba237f97e0..ba470a6b2428 100644 --- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AccountCache.kt +++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AccountCache.kt @@ -17,15 +17,6 @@ import net.mullvad.mullvadvpn.model.GetAccountDataResult import net.mullvad.talpid.util.EventNotifier class AccountCache(private val endpoint: ServiceEndpoint) { - companion object { - private sealed class Command { - object CreateAccount : Command() - - data class Login(val account: String) : Command() - - object Logout : Command() - } - } private val commandChannel = spawnActor() @@ -179,4 +170,14 @@ class AccountCache(private val endpoint: ServiceEndpoint) { private suspend fun fetchAccountData(accountToken: String): GetAccountDataResult { return daemon.await().getAccountData(accountToken) } + + companion object { + private sealed class Command { + object CreateAccount : Command() + + data class Login(val account: String) : Command() + + object Logout : Command() + } + } } diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AuthTokenCache.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AuthTokenCache.kt index 6506c0469d6c..08b0943c4d6f 100644 --- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AuthTokenCache.kt +++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AuthTokenCache.kt @@ -11,12 +11,6 @@ import net.mullvad.mullvadvpn.lib.ipc.Event import net.mullvad.mullvadvpn.lib.ipc.Request class AuthTokenCache(endpoint: ServiceEndpoint) { - companion object { - private enum class Command { - Fetch - } - } - private val daemon = endpoint.intermittentDaemon private val requestQueue = spawnActor() @@ -46,4 +40,10 @@ class AuthTokenCache(endpoint: ServiceEndpoint) { // Closed sender, so stop the actor } } + + companion object { + private enum class Command { + Fetch + } + } } diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/LocationInfoCache.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/LocationInfoCache.kt index e050421699b1..2d06cc109f73 100644 --- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/LocationInfoCache.kt +++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/LocationInfoCache.kt @@ -22,12 +22,6 @@ import net.mullvad.mullvadvpn.service.util.ExponentialBackoff import net.mullvad.talpid.tunnel.ActionAfterDisconnect class LocationInfoCache(private val endpoint: ServiceEndpoint) { - companion object { - private enum class RequestFetch { - ForRealLocation, - ForRelayLocation, - } - } private val fetchRetryDelays = ExponentialBackoff().apply { @@ -135,4 +129,11 @@ class LocationInfoCache(private val endpoint: ServiceEndpoint) { selectedRelayLocation = constraint?.value?.toGeographicLocationConstraint()?.location } + + companion object { + private enum class RequestFetch { + ForRealLocation, + ForRelayLocation, + } + } } diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/RelayListListener.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/RelayListListener.kt index 1abf64907c89..09f90a44d5da 100644 --- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/RelayListListener.kt +++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/RelayListListener.kt @@ -19,12 +19,6 @@ import net.mullvad.mullvadvpn.model.WireguardConstraints import net.mullvad.mullvadvpn.service.MullvadDaemon class RelayListListener(endpoint: ServiceEndpoint) { - companion object { - private enum class Command { - SetRelayLocation, - SetWireguardConstraints - } - } private val commandChannel = spawnActor() private val daemon = endpoint.intermittentDaemon @@ -105,4 +99,11 @@ class RelayListListener(endpoint: ServiceEndpoint) { daemon.await().updateRelaySettings(update) } + + companion object { + private enum class Command { + SetRelayLocation, + SetWireguardConstraints + } + } } diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ServiceEndpoint.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ServiceEndpoint.kt index e9aa8d432860..fe7ddb75e9cc 100644 --- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ServiceEndpoint.kt +++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ServiceEndpoint.kt @@ -27,13 +27,6 @@ class ServiceEndpoint( val connectivityListener: ConnectivityListener, context: Context ) { - companion object { - sealed class Command { - data class RegisterListener(val listener: Messenger) : Command() - - data class UnregisterListener(val listenerId: Int) : Command() - } - } private val listeners = mutableMapOf() private val commands: SendChannel = startRegistrator() @@ -165,4 +158,12 @@ class ServiceEndpoint( return listenerId } + + companion object { + sealed class Command { + data class RegisterListener(val listener: Messenger) : Command() + + data class UnregisterListener(val listenerId: Int) : Command() + } + } } diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/AccountExpiryNotification.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/AccountExpiryNotification.kt index b6085221f176..6ed6a5b72ec0 100644 --- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/AccountExpiryNotification.kt +++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/AccountExpiryNotification.kt @@ -28,11 +28,6 @@ class AccountExpiryNotification( val daemon: Intermittent, val accountCache: AccountCache ) { - companion object { - val NOTIFICATION_ID: Int = 2 - val REMAINING_TIME_FOR_REMINDERS = Duration.standardDays(2) - val TIME_BETWEEN_CHECKS: Long = 12 /* h */ * 60 /* min */ * 60 /* s */ * 1000 /* ms */ - } private val jobTracker = JobTracker() private val resources = context.resources @@ -143,4 +138,10 @@ class AccountExpiryNotification( private fun getRemainingText(pluralId: Int, quantity: Int): String { return resources.getQuantityString(pluralId, quantity, quantity) } + + companion object { + val NOTIFICATION_ID: Int = 2 + val REMAINING_TIME_FOR_REMINDERS = Duration.standardDays(2) + val TIME_BETWEEN_CHECKS: Long = 12 /* h */ * 60 /* min */ * 60 /* s */ * 1000 /* ms */ + } } diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/TunnelStateNotification.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/TunnelStateNotification.kt index a19358f95eba..97bddc860805 100644 --- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/TunnelStateNotification.kt +++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/TunnelStateNotification.kt @@ -18,10 +18,6 @@ import net.mullvad.talpid.tunnel.ActionAfterDisconnect import net.mullvad.talpid.tunnel.ErrorStateCause class TunnelStateNotification(val context: Context) { - companion object { - val NOTIFICATION_ID: Int = 1 - } - private val channel = NotificationChannel( context, @@ -149,4 +145,8 @@ class TunnelStateNotification(val context: Context) { return NotificationCompat.Action(action.icon, label, pendingIntent) } + + companion object { + val NOTIFICATION_ID: Int = 1 + } } diff --git a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/TunnelStateNotificationAction.kt b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/TunnelStateNotificationAction.kt index c415940ea87c..c836c765f6bb 100644 --- a/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/TunnelStateNotificationAction.kt +++ b/android/service/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/TunnelStateNotificationAction.kt @@ -12,28 +12,6 @@ enum class TunnelStateNotificationAction { Cancel, Dismiss; - companion object { - fun from(tunnelState: TunnelState) = - when (tunnelState) { - is TunnelState.Disconnected -> Connect - is TunnelState.Connecting -> Cancel - is TunnelState.Connected -> Disconnect - is TunnelState.Disconnecting -> { - when (tunnelState.actionAfterDisconnect) { - ActionAfterDisconnect.Reconnect -> Cancel - else -> Connect - } - } - is TunnelState.Error -> { - if (tunnelState.errorState.isBlocking) { - Disconnect - } else { - Dismiss - } - } - } - } - val text get() = when (this) { @@ -56,4 +34,26 @@ enum class TunnelStateNotificationAction { Connect -> R.drawable.icon_notification_connect else -> R.drawable.icon_notification_disconnect } + + companion object { + fun from(tunnelState: TunnelState) = + when (tunnelState) { + is TunnelState.Disconnected -> Connect + is TunnelState.Connecting -> Cancel + is TunnelState.Connected -> Disconnect + is TunnelState.Disconnecting -> { + when (tunnelState.actionAfterDisconnect) { + ActionAfterDisconnect.Reconnect -> Cancel + else -> Connect + } + } + is TunnelState.Error -> { + if (tunnelState.errorState.isBlocking) { + Disconnect + } else { + Dismiss + } + } + } + } } diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/ClassTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/ClassTests.kt new file mode 100644 index 000000000000..cc737250f65a --- /dev/null +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/ClassTests.kt @@ -0,0 +1,21 @@ +package net.mullvad.mullvadvpn.test.arch.classes + +import com.lemonappdev.konsist.api.verify.assert +import net.mullvad.mullvadvpn.test.arch.extensions.projectScope +import org.junit.Test + +class ClassTests { + @Test + fun `companion object is last declaration in the class`() { + projectScope().classes(includeNested = true).assert { + val companionObject = + it.objects(includeNested = false).lastOrNull { obj -> obj.hasCompanionModifier } + if (companionObject != null) { + it.declarations(includeNested = false, includeLocal = false).last() == + companionObject + } else { + true + } + } + } +} From 469e0efbec57ce256973d1329f5a616e2234e742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20G=C3=B6ransson?= Date: Fri, 29 Sep 2023 20:10:05 +0200 Subject: [PATCH 6/9] Add test ensure all composables are in compose package --- .../test/arch/classes/DataClassTests.kt | 17 +++++++++++++++++ .../test/arch/compose/ComposeTests.kt | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/DataClassTests.kt create mode 100644 android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/compose/ComposeTests.kt diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/DataClassTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/DataClassTests.kt new file mode 100644 index 000000000000..248eef5a1006 --- /dev/null +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/DataClassTests.kt @@ -0,0 +1,17 @@ +package net.mullvad.mullvadvpn.test.arch.classes + +import com.lemonappdev.konsist.api.ext.list.modifierprovider.withDataModifier +import com.lemonappdev.konsist.api.verify.assert +import net.mullvad.mullvadvpn.test.arch.extensions.projectScope +import org.junit.Ignore +import org.junit.Test + +class DataClasses { + @Ignore("Code needs clean up") + @Test + fun `data classes use only immutable parameters`() { + projectScope().classes(includeNested = true).withDataModifier().assert { + it.properties(includeNested = true).all { property -> property.hasValModifier } + } + } +} diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/compose/ComposeTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/compose/ComposeTests.kt new file mode 100644 index 000000000000..970174d269b4 --- /dev/null +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/compose/ComposeTests.kt @@ -0,0 +1,17 @@ +package net.mullvad.mullvadvpn.test.arch.compose + +import androidx.compose.runtime.Composable +import com.lemonappdev.konsist.api.Konsist +import com.lemonappdev.konsist.api.ext.list.withAllAnnotationsOf +import com.lemonappdev.konsist.api.verify.assert +import org.junit.Test + +class ComposeTests { + @Test + fun `all app composables are in the compose packages`() { + allAppComposeFunctions().assert { it.resideInPackage("net.mullvad.mullvadvpn.compose..") } + } + + private fun allAppComposeFunctions() = + Konsist.scopeFromProduction("app").functions().withAllAnnotationsOf(Composable::class) +} From 23517aa5dda6ba8360a6fa9ce408aa63ae5751ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20G=C3=B6ransson?= Date: Mon, 2 Oct 2023 08:25:15 +0200 Subject: [PATCH 7/9] Fix data class issues --- .../screen/SelectLocationScreenTest.kt | 10 +++++--- .../compose/screen/SelectLocationScreen.kt | 1 - .../net/mullvad/mullvadvpn/relaylist/Relay.kt | 4 +--- .../mullvad/mullvadvpn/relaylist/RelayCity.kt | 2 +- .../mullvadvpn/relaylist/RelayCountry.kt | 2 +- .../mullvad/mullvadvpn/relaylist/RelayItem.kt | 2 +- .../relaylist/RelayListExtensions.kt | 23 +++++++++++++------ .../model/RelayConstraintsUpdate.kt | 4 ++-- .../mullvadvpn/model/RelaySettingsUpdate.kt | 2 +- .../test/arch/classes/DataClassTests.kt | 15 ++++++------ .../test/mockapi/MockApiDispatcher.kt | 3 +++ 11 files changed, 41 insertions(+), 27 deletions(-) diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreenTest.kt index 4844177b45ac..106d960d1fa2 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreenTest.kt @@ -82,9 +82,13 @@ class SelectLocationScreenTest { uiState = SelectLocationUiState.ShowData( countries = - DUMMY_RELAY_COUNTRIES.apply { - this[0].expanded = true - this[0].cities[0].expanded = true + DUMMY_RELAY_COUNTRIES.let { + val cities = it[0].cities.toMutableList() + val city = cities.removeAt(0) + cities.add(0, city.copy(expanded = true)) + it.toMutableList()[0] = + it[0].copy(expanded = true, cities = cities.toList()) + it }, selectedRelay = DUMMY_RELAY_COUNTRIES[0].cities[0].relays[0] ), diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt index b7f23ea76849..fec20ceb185c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt @@ -18,7 +18,6 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/Relay.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/Relay.kt index f3c3a3cc7cc6..9bdb59168b1a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/Relay.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/Relay.kt @@ -12,7 +12,5 @@ data class Relay( override val type = RelayItemType.Relay override val hasChildren = false - override var expanded - get() = false - set(_) {} + override val expanded = false } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayCity.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayCity.kt index d1a3332a78b6..e3693963f81f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayCity.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayCity.kt @@ -6,7 +6,7 @@ data class RelayCity( override val name: String, override val code: String, override val location: GeographicLocationConstraint, - override var expanded: Boolean, + override val expanded: Boolean, val relays: List ) : RelayItem { override val type = RelayItemType.City diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayCountry.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayCountry.kt index d418fd3a4ae3..3ad1d5962fcc 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayCountry.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayCountry.kt @@ -5,7 +5,7 @@ import net.mullvad.mullvadvpn.model.GeographicLocationConstraint data class RelayCountry( override val name: String, override val code: String, - override var expanded: Boolean, + override val expanded: Boolean, val cities: List ) : RelayItem { override val type = RelayItemType.Country diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItem.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItem.kt index f4387e96471c..3d38d6b0685d 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItem.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayItem.kt @@ -13,5 +13,5 @@ interface RelayItem { val locationName: String get() = name - var expanded: Boolean + val expanded: Boolean } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListExtensions.kt index 401f03d744d3..39618f96034e 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListExtensions.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayListExtensions.kt @@ -122,8 +122,9 @@ fun List.filterOnSearchTerm( // Finally if the city has not already been added to the filtered list, add it, but // do not expand it yet. if (relayCity.name.contains(other = searchTerm, ignoreCase = true)) { - if (filteredCountries.containsKey(relayCountry.code)) { - filteredCountries[relayCountry.code]?.expanded = true + val value = filteredCountries[relayCountry.code] + if (value != null) { + filteredCountries[relayCountry.code] = value.copy(expanded = true) } else { filteredCountries[relayCountry.code] = relayCountry.copy(expanded = true, cities = cities) @@ -141,15 +142,23 @@ fun List.filterOnSearchTerm( // if so expand it, if not add it to the filtered list and expand it. // Finally add the relay to the list. if (relay.name.contains(other = searchTerm, ignoreCase = true)) { - if (filteredCountries.containsKey(relayCountry.code)) { - filteredCountries[relayCountry.code]?.expanded = true + val value = filteredCountries[relayCountry.code] + if (value != null) { + filteredCountries[relayCountry.code] = value.copy(expanded = true) } else { filteredCountries[relayCountry.code] = relayCountry.copy(expanded = true, cities = cities) } - val city = cities.find { it.code == relayCity.code } - city?.let { city.expanded = true } - ?: run { cities.add(relayCity.copy(expanded = true, relays = relays)) } + val cityIndex = cities.indexOfFirst { it.code == relayCity.code } + + // No city found + if (cityIndex < 0) { + cities.add(relayCity.copy(expanded = true, relays = relays)) + } else { + // Update found city as expanded + cities[cityIndex] = cities[cityIndex].copy(expanded = true) + } + relays.add(relay.copy()) } } diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayConstraintsUpdate.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayConstraintsUpdate.kt index bd5bfb9605d1..991ae7dbdeef 100644 --- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayConstraintsUpdate.kt +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayConstraintsUpdate.kt @@ -1,6 +1,6 @@ package net.mullvad.mullvadvpn.model data class RelayConstraintsUpdate( - var location: Constraint?, - var wireguardConstraints: WireguardConstraints? + val location: Constraint?, + val wireguardConstraints: WireguardConstraints? ) diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/RelaySettingsUpdate.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/RelaySettingsUpdate.kt index a7d4c0b03d0a..f8d27f115e37 100644 --- a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/RelaySettingsUpdate.kt +++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/model/RelaySettingsUpdate.kt @@ -3,5 +3,5 @@ package net.mullvad.mullvadvpn.model sealed class RelaySettingsUpdate { object CustomTunnelEndpoint : RelaySettingsUpdate() - data class Normal(var constraints: RelayConstraintsUpdate) : RelaySettingsUpdate() + data class Normal(val constraints: RelayConstraintsUpdate) : RelaySettingsUpdate() } diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/DataClassTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/DataClassTests.kt index 248eef5a1006..3fd5eff97962 100644 --- a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/DataClassTests.kt +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/DataClassTests.kt @@ -1,17 +1,18 @@ package net.mullvad.mullvadvpn.test.arch.classes import com.lemonappdev.konsist.api.ext.list.modifierprovider.withDataModifier -import com.lemonappdev.konsist.api.verify.assert +import com.lemonappdev.konsist.api.ext.list.properties +import com.lemonappdev.konsist.api.verify.assertNot import net.mullvad.mullvadvpn.test.arch.extensions.projectScope -import org.junit.Ignore import org.junit.Test class DataClasses { - @Ignore("Code needs clean up") @Test - fun `data classes use only immutable parameters`() { - projectScope().classes(includeNested = true).withDataModifier().assert { - it.properties(includeNested = true).all { property -> property.hasValModifier } - } + fun `data classes use only immutable declarations`() { + projectScope() + .classes(includeNested = true) + .withDataModifier() + .properties(includeNested = false, includeLocal = false) + .assertNot { it.hasVarModifier } } } diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiDispatcher.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiDispatcher.kt index 06f716403465..6a604bb09f53 100644 --- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiDispatcher.kt +++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiDispatcher.kt @@ -8,7 +8,10 @@ import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_ACCESS_TOKEN import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_DEVICE_NAME import net.mullvad.mullvadvpn.test.mockapi.constant.DUMMY_ID import net.mullvad.mullvadvpn.test.mockapi.constant.LOG_TAG +import net.mullvad.mullvadvpn.test.mockapi.util.accessTokenJsonResponse +import net.mullvad.mullvadvpn.test.mockapi.util.accountInfoJson import net.mullvad.mullvadvpn.test.mockapi.util.currentUtcTimeWithOffsetZero +import net.mullvad.mullvadvpn.test.mockapi.util.deviceJson import okhttp3.mockwebserver.Dispatcher import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.RecordedRequest From 3f6a24dc2cf5437d90754fe086c9e3ba40e4f53d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20G=C3=B6ransson?= Date: Mon, 2 Oct 2023 15:38:58 +0200 Subject: [PATCH 8/9] Align test syntax --- .../net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt | 2 +- .../mullvad/mullvadvpn/viewmodel/DeviceListViewModel.kt | 5 +++-- .../mullvadvpn/viewmodel/SelectLocationViewModel.kt | 4 ++-- .../mullvad/mullvadvpn/viewmodel/SettingsViewModel.kt | 2 +- .../mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt | 2 +- .../mullvad/mullvadvpn/test/arch/ArchitectureTests.kt | 7 +++---- .../net/mullvad/mullvadvpn/test/arch/GeneralTests.kt | 9 +++------ .../net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt | 9 +++------ .../mullvad/mullvadvpn/test/arch/classes/ClassTests.kt | 7 +++---- .../mullvadvpn/test/arch/classes/DataClassTests.kt | 7 +++---- .../mullvadvpn/test/arch/compose/ComposePreviewTests.kt | 8 +++----- .../mullvad/mullvadvpn/test/arch/compose/ComposeTests.kt | 3 +-- .../mullvadvpn/test/arch/extensions/KonsistExtensions.kt | 5 ----- 13 files changed, 27 insertions(+), 43 deletions(-) delete mode 100644 android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/extensions/KonsistExtensions.kt diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt index 4099e524807d..a03de806b4b6 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AccountViewModel.kt @@ -38,7 +38,7 @@ class AccountViewModel( } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), AccountUiState.default()) - @Suppress("konsist.ensurePublicPropertiesUsePermittedNames") + @Suppress("konsist.ensure public properties use permitted names") val enterTransitionEndAction = _enterTransitionEndAction.asSharedFlow() fun onManageAccountClick() { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceListViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceListViewModel.kt index c554bd4daa48..98648e0015bb 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceListViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceListViewModel.kt @@ -37,10 +37,11 @@ class DeviceListViewModel( private val _loadingDevices = MutableStateFlow>(emptyList()) private val _toastMessages = MutableSharedFlow(extraBufferCapacity = 1) - @Suppress("konsist.ensurePublicPropertiesUsePermittedNames") + @Suppress("konsist.ensure public properties use permitted names") val toastMessages = _toastMessages.asSharedFlow() - @Suppress("konsist.ensurePublicPropertiesUsePermittedNames") var accountToken: String? = null + @Suppress("konsist.ensure public properties use permitted names") + var accountToken: String? = null private var cachedDeviceList: List? = null val uiState = diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt index 2ae361c96440..2507e9fb1903 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt @@ -66,9 +66,9 @@ class SelectLocationViewModel(private val serviceConnectionManager: ServiceConne SelectLocationUiState.Loading ) - @Suppress("konsist.ensurePublicPropertiesUsePermittedNames") + @Suppress("konsist.ensure public properties use permitted names") val uiCloseAction = _closeAction.asSharedFlow() - @Suppress("konsist.ensurePublicPropertiesUsePermittedNames") + @Suppress("konsist.ensure public properties use permitted names") val enterTransitionEndAction = _enterTransitionEndAction.asSharedFlow() fun selectRelay(relayItem: RelayItem?) { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SettingsViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SettingsViewModel.kt index 89adbe20befd..fb357dfe2a3a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SettingsViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SettingsViewModel.kt @@ -45,7 +45,7 @@ class SettingsViewModel( SettingsUiState(appVersion = "", isLoggedIn = false, isUpdateAvailable = false) ) - @Suppress("konsist.ensurePublicPropertiesUsePermittedNames") + @Suppress("konsist.ensure public properties use permitted names") val enterTransitionEndAction = _enterTransitionEndAction.asSharedFlow() fun onTransitionAnimationEnd() { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt index 2c8e30f2b479..9c25fe824f75 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt @@ -51,7 +51,7 @@ class VpnSettingsViewModel( ) : ViewModel() { private val _toastMessages = MutableSharedFlow(extraBufferCapacity = 1) - @Suppress("konsist.ensurePublicPropertiesUsePermittedNames") + @Suppress("konsist.ensure public properties use permitted names") val toastMessages = _toastMessages.asSharedFlow() private val dialogState = diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ArchitectureTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ArchitectureTests.kt index ecb2fa18bb1d..2a7e1e205e34 100644 --- a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ArchitectureTests.kt +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ArchitectureTests.kt @@ -8,11 +8,10 @@ import org.junit.Test class ArchitectureTests { @Test - fun `domain layer depends on nothing`() { + fun `ensure model layer depends on nothing`() = Konsist.scopeFromProduction().assertArchitecture { - val domain = Layer("Domain", "net.mullvad.mullvadvpn.model..") + val model = Layer("Model", "net.mullvad.mullvadvpn.model..") - domain.dependsOnNothing() + model.dependsOnNothing() } - } } diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/GeneralTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/GeneralTests.kt index a40f93c5f52a..60842537c3af 100644 --- a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/GeneralTests.kt +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/GeneralTests.kt @@ -8,20 +8,17 @@ import org.junit.Test class GeneralTests { @Test - fun `package name must match file path`() { + fun `ensure package name must match file path`() = Konsist.scopeFromProject().packages.assert { it.hasMatchingPath } - } @Test - fun `no field should have 'm' prefix`() { + fun `ensure no field should have 'm' prefix`() = Konsist.scopeFromProject().classes().properties().assertNot { val secondCharacterIsUppercase = it.name.getOrNull(1)?.isUpperCase() ?: false it.name.startsWith('m') && secondCharacterIsUppercase } - } @Test - fun `no empty files allowed`() { + fun `ensure no empty files allowed`() = Konsist.scopeFromProject().files.assertNot { it.text.isEmpty() } - } } diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt index 8347c799d71f..a474ec3eb6b3 100644 --- a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt @@ -12,26 +12,23 @@ import org.junit.Test class ViewModelTests { @Test - fun ensureViewModelsHaveViewModelSuffix() { + fun ensureViewModelsHaveViewModelSuffix() = allViewModels().assert { it.name.endsWith("ViewModel") } - } // The purpose of this check is to both keep the naming consistent and also to avoid exposing // properties that shouldn't be exposed. @Test - fun ensurePublicPropertiesUsePermittedNames() { + fun `ensure public properties use permitted names`() = allViewModels().properties(includeNested = false).withPublicOrDefaultModifier().assert { property -> property.name == "uiState" || property.name == "uiSideEffect" } - } @Test - fun ensurePublicFunctionsHaveNoReturnType() { + fun `ensure public functions have no return type`() = allViewModels().functions().withPublicOrDefaultModifier().assertNot { function -> function.hasReturnType() } - } private fun allViewModels() = Konsist.scopeFromProject().classes().withAllParentsOf(ViewModel::class) diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/ClassTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/ClassTests.kt index cc737250f65a..918139bf2458 100644 --- a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/ClassTests.kt +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/ClassTests.kt @@ -1,13 +1,13 @@ package net.mullvad.mullvadvpn.test.arch.classes +import com.lemonappdev.konsist.api.Konsist import com.lemonappdev.konsist.api.verify.assert -import net.mullvad.mullvadvpn.test.arch.extensions.projectScope import org.junit.Test class ClassTests { @Test - fun `companion object is last declaration in the class`() { - projectScope().classes(includeNested = true).assert { + fun `ensure companion object is last declaration in the class`() = + Konsist.scopeFromProject().classes(includeNested = true).assert { val companionObject = it.objects(includeNested = false).lastOrNull { obj -> obj.hasCompanionModifier } if (companionObject != null) { @@ -17,5 +17,4 @@ class ClassTests { true } } - } } diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/DataClassTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/DataClassTests.kt index 3fd5eff97962..cc2f7262b128 100644 --- a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/DataClassTests.kt +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/DataClassTests.kt @@ -1,18 +1,17 @@ package net.mullvad.mullvadvpn.test.arch.classes +import com.lemonappdev.konsist.api.Konsist import com.lemonappdev.konsist.api.ext.list.modifierprovider.withDataModifier import com.lemonappdev.konsist.api.ext.list.properties import com.lemonappdev.konsist.api.verify.assertNot -import net.mullvad.mullvadvpn.test.arch.extensions.projectScope import org.junit.Test class DataClasses { @Test - fun `data classes use only immutable declarations`() { - projectScope() + fun `ensure data classes only use immutable properties`() = + Konsist.scopeFromProject() .classes(includeNested = true) .withDataModifier() .properties(includeNested = false, includeLocal = false) .assertNot { it.hasVarModifier } - } } diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/compose/ComposePreviewTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/compose/ComposePreviewTests.kt index 64b221d0c073..5f8b1fef8026 100644 --- a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/compose/ComposePreviewTests.kt +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/compose/ComposePreviewTests.kt @@ -8,15 +8,13 @@ import org.junit.Test class ComposePreviewTests { @Test - fun `all preview functions are private`() { + fun `ensure all preview functions are private`() = allPreviewFunctions().assert { it.hasPrivateModifier } - } @Test - fun `all preview functions are prefixed with Preview`() { + fun `ensure all preview functions are prefixed with 'Preview'`() = allPreviewFunctions().assert { it.name.startsWith("Preview") } - } private fun allPreviewFunctions() = - Konsist.scopeFromProduction().functions().withAllAnnotationsOf(Preview::class) + Konsist.scopeFromProduction("app").functions().withAllAnnotationsOf(Preview::class) } diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/compose/ComposeTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/compose/ComposeTests.kt index 970174d269b4..025f10d11f25 100644 --- a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/compose/ComposeTests.kt +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/compose/ComposeTests.kt @@ -8,9 +8,8 @@ import org.junit.Test class ComposeTests { @Test - fun `all app composables are in the compose packages`() { + fun `ensure all app composables are in the compose package`() = allAppComposeFunctions().assert { it.resideInPackage("net.mullvad.mullvadvpn.compose..") } - } private fun allAppComposeFunctions() = Konsist.scopeFromProduction("app").functions().withAllAnnotationsOf(Composable::class) diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/extensions/KonsistExtensions.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/extensions/KonsistExtensions.kt deleted file mode 100644 index b228575b467c..000000000000 --- a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/extensions/KonsistExtensions.kt +++ /dev/null @@ -1,5 +0,0 @@ -package net.mullvad.mullvadvpn.test.arch.extensions - -import com.lemonappdev.konsist.api.Konsist - -fun projectScope() = Konsist.scopeFromProject() From 54a8e7178e255d276007260e418ae9d32966af36 Mon Sep 17 00:00:00 2001 From: Albin Date: Mon, 2 Oct 2023 16:15:20 +0200 Subject: [PATCH 9/9] Add konsist test name check Checks that the tests are prefixed with "ensure " --- .../mullvad/mullvadvpn/test/arch/KonsistTests.kt | 14 ++++++++++++++ .../mullvad/mullvadvpn/test/arch/ViewModelTests.kt | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/KonsistTests.kt diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/KonsistTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/KonsistTests.kt new file mode 100644 index 000000000000..f2954bdb687b --- /dev/null +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/KonsistTests.kt @@ -0,0 +1,14 @@ +package net.mullvad.mullvadvpn.test.arch + +import com.lemonappdev.konsist.api.Konsist +import com.lemonappdev.konsist.api.ext.list.withAnnotationOf +import com.lemonappdev.konsist.api.verify.assert +import org.junit.Test + +class KonsistTests { + @Test + fun `ensure konsist tests have 'ensure ' prefix`() = + Konsist.scopeFromModule("test/arch").functions().withAnnotationOf(Test::class).assert { + it.hasNameStartingWith("ensure ") + } +} diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt index a474ec3eb6b3..0f23e52a4379 100644 --- a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt +++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/ViewModelTests.kt @@ -12,7 +12,7 @@ import org.junit.Test class ViewModelTests { @Test - fun ensureViewModelsHaveViewModelSuffix() = + fun `ensure view models have view model suffix`() = allViewModels().assert { it.name.endsWith("ViewModel") } // The purpose of this check is to both keep the naming consistent and also to avoid exposing