From 5666383134b9a85d482d8459c73d8dcc9110ec8d Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 28 Sep 2022 18:19:25 +0300 Subject: [PATCH] Add unit test for desktop and web user agents. --- .../api/session/crypto/model/DeviceInfo.kt | 4 +- .../devices/v2/ParseDeviceUserAgentUseCase.kt | 57 +++++++++++++++++-- .../v2/overview/GetDeviceFullInfoUseCase.kt | 1 - .../devices/v2/DevicesViewModelTest.kt | 1 - .../v2/ParseDeviceUserAgentUseCaseTest.kt | 38 +++++++++++++ 5 files changed, 91 insertions(+), 10 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/DeviceInfo.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/DeviceInfo.kt index 338a594011..500d016002 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/DeviceInfo.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/DeviceInfo.kt @@ -54,10 +54,10 @@ data class DeviceInfo( @Json(name = "last_seen_ip") val lastSeenIp: String? = null, - @Json(name="org.matrix.msc3852.last_seen_user_agent") + @Json(name = "org.matrix.msc3852.last_seen_user_agent") val unstableLastSeenUserAgent: String? = null, - @Json(name="last_seen_user_agent") + @Json(name = "last_seen_user_agent") val lastSeenUserAgent: String? = null, ) : DatedObject { diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt index fe02aa6400..eb1daaea0e 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCase.kt @@ -17,6 +17,7 @@ package im.vector.app.features.settings.devices.v2 import im.vector.app.features.settings.devices.v2.list.DeviceType +import org.matrix.android.sdk.api.extensions.orFalse import javax.inject.Inject class ParseDeviceUserAgentUseCase @Inject constructor() { @@ -60,12 +61,29 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { } private fun parseDesktopUserAgent(userAgent: String): DeviceUserAgent { - val appInfoSegments = userAgent.substringBeforeLast(" ").substringAfterLast(" ").split("/") - val appName = appInfoSegments.getOrNull(0) - val appVersion = appInfoSegments.getOrNull(1) - val deviceInfoSegments = userAgent.substringAfter("(").substringBefore(")").split("; ") - val deviceOperatingSystem = deviceInfoSegments.getOrNull(1) - return DeviceUserAgent(DeviceType.DESKTOP, null, deviceOperatingSystem, appName, appVersion) + val browserSegments = userAgent.split(" ") + val browserName = when { + isFirefox(browserSegments) -> { + "Firefox" + } + isMobile(browserSegments) -> { + getMobileBrowserName(browserSegments) + } + isSafari(browserSegments) -> { + "Safari" + } + else -> { + getRegularBrowserName(browserSegments) + } + } + + val deviceOperatingSystemSegments = userAgent.substringAfter("(").substringBefore(")").split("; ") + val deviceOperatingSystem = if (deviceOperatingSystemSegments.getOrNull(1)?.startsWith("Android").orFalse()) { + deviceOperatingSystemSegments.getOrNull(1) + } else { + deviceOperatingSystemSegments.getOrNull(0) + } + return DeviceUserAgent(DeviceType.DESKTOP, browserName, deviceOperatingSystem, null, null) } private fun parseWebUserAgent(userAgent: String): DeviceUserAgent { @@ -78,6 +96,33 @@ class ParseDeviceUserAgentUseCase @Inject constructor() { return DeviceUserAgent(DeviceType.UNKNOWN) } + private fun isFirefox(browserSegments: List): Boolean { + return browserSegments.lastOrNull()?.startsWith("Firefox").orFalse() + } + + private fun isSafari(browserSegments: List): Boolean { + return browserSegments.lastOrNull()?.startsWith("Safari").orFalse() && + browserSegments.getOrNull(browserSegments.size - 2)?.startsWith("Version").orFalse() + } + + private fun isMobile(browserSegments: List): Boolean { + return browserSegments.lastOrNull()?.startsWith("Safari").orFalse() && + browserSegments.getOrNull(browserSegments.size - 2) == "Mobile" + } + + private fun getMobileBrowserName(browserSegments: List): String? { + val possibleBrowserName = browserSegments.getOrNull(browserSegments.size - 3)?.split("/")?.firstOrNull() + return if (possibleBrowserName == "Version") { + "Safari" + } else { + possibleBrowserName + } + } + + private fun getRegularBrowserName(browserSegments: List): String? { + return browserSegments.getOrNull(browserSegments.size - 2)?.split("/")?.firstOrNull() + } + companion object { // Element dbg/1.5.0-dev (Xiaomi; Mi 9T; Android 11; RKQ1.200826.002 test-keys; Flavour GooglePlay; MatrixAndroidSdk2 1.5.0) // Legacy : Element/1.0.0 (Linux; U; Android 6.0.1; SM-A510F Build/MMB29; Flavour GPlay; MatrixAndroidSdk2 1.0) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt index cfaa6aed85..fd612308fc 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt @@ -26,7 +26,6 @@ import im.vector.app.features.settings.devices.v2.verification.GetEncryptionTrus import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.emptyFlow -import okhttp3.internal.userAgent import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.flow.unwrap import javax.inject.Inject diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt index e5a3962c6c..3b011ae9bb 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt @@ -37,7 +37,6 @@ import io.mockk.runs import io.mockk.unmockkAll import io.mockk.verify import kotlinx.coroutines.flow.flowOf -import okhttp3.internal.userAgent import org.junit.After import org.junit.Before import org.junit.Rule diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt index 1e73417ba2..8eea1f7136 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/ParseDeviceUserAgentUseCaseTest.kt @@ -46,6 +46,30 @@ private val AN_EXPECTED_RESULT_LIST_FOR_IOS = listOf( DeviceUserAgent(DeviceType.MOBILE, "iPhone XS Max", "iOS 15.2", "Element", "1.8.21"), ) +private val A_USER_AGENT_LIST_FOR_DESKTOP = listOf( + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) ElementNightly/2022091301 Chrome/104.0.5112.102 Electron/20.1.1 Safari/537.36", + "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) ElementNightly/2022091301 Chrome/104.0.5112.102 Electron/20.1.1 Safari/537.36", +) +private val AN_EXPECTED_RESULT_LIST_FOR_DESKTOP = listOf( + DeviceUserAgent(DeviceType.DESKTOP, "Electron", "Macintosh", null, null), + DeviceUserAgent(DeviceType.DESKTOP, "Electron", "Windows NT 10.0", null, null), +) + +private val A_USER_AGENT_LIST_FOR_WEB = listOf( + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36", + "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:39.0) Gecko/20100101 Firefox/39.0", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/600.3.18 (KHTML, like Gecko) Version/8.0.3 Safari/600.3.18", + "Mozilla/5.0 (Linux; Android 9; SM-G973U Build/PPR1.180610.011) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36", + ) +private val AN_EXPECTED_RESULT_LIST_FOR_WEB = listOf( + DeviceUserAgent(DeviceType.WEB, "Chrome", "Macintosh", null, null), + DeviceUserAgent(DeviceType.WEB, "Chrome", "Windows NT 10.0", null, null), + DeviceUserAgent(DeviceType.WEB, "Firefox", "Macintosh", null, null), + DeviceUserAgent(DeviceType.WEB, "Safari", "Macintosh", null, null), + DeviceUserAgent(DeviceType.WEB, "Chrome", "Android 9", null, null), +) + class ParseDeviceUserAgentUseCaseTest { private val parseDeviceUserAgentUseCase = ParseDeviceUserAgentUseCase() @@ -63,4 +87,18 @@ class ParseDeviceUserAgentUseCaseTest { parseDeviceUserAgentUseCase.execute(userAgent) shouldBeEqualTo AN_EXPECTED_RESULT_LIST_FOR_IOS[index] } } + + @Test + fun `given a Desktop user agent then it should be parsed as expected`() { + A_USER_AGENT_LIST_FOR_DESKTOP.forEachIndexed { index, userAgent -> + parseDeviceUserAgentUseCase.execute(userAgent) shouldBeEqualTo AN_EXPECTED_RESULT_LIST_FOR_DESKTOP[index] + } + } + + @Test + fun `given a Web user agent then it should be parsed as expected`() { + A_USER_AGENT_LIST_FOR_WEB.forEachIndexed { index, userAgent -> + parseDeviceUserAgentUseCase.execute(userAgent) shouldBeEqualTo AN_EXPECTED_RESULT_LIST_FOR_WEB[index] + } + } }