diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/ENFClient.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/ENFClient.kt index c3de7abd0615b66f64d5eb6255c6d1c255e571e8..7351e5219b5222bbdc33e716cc3ca065cb2cca2e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/ENFClient.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/ENFClient.kt @@ -9,6 +9,7 @@ import de.rki.coronawarnapp.nearby.modules.detectiontracker.TrackedExposureDetec import de.rki.coronawarnapp.nearby.modules.diagnosiskeyprovider.DiagnosisKeyProvider import de.rki.coronawarnapp.nearby.modules.locationless.ScanningSupport import de.rki.coronawarnapp.nearby.modules.tracing.TracingStatus +import de.rki.coronawarnapp.nearby.modules.version.ENFVersion import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import org.joda.time.Instant @@ -23,8 +24,9 @@ class ENFClient @Inject constructor( private val diagnosisKeyProvider: DiagnosisKeyProvider, private val tracingStatus: TracingStatus, private val scanningSupport: ScanningSupport, - private val exposureDetectionTracker: ExposureDetectionTracker -) : DiagnosisKeyProvider, TracingStatus, ScanningSupport { + private val exposureDetectionTracker: ExposureDetectionTracker, + private val enfVersion: ENFVersion +) : DiagnosisKeyProvider, TracingStatus, ScanningSupport, ENFVersion by enfVersion { // TODO Remove this once we no longer need direct access to the ENF Client, // i.e. in **[InternalExposureNotificationClient]** diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/ENFModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/ENFModule.kt index 9d98d5b33bf809dbc19995310857d2cda775fcb6..7ccdbd157982b98026afd3de3a148d8c06ef3b02 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/ENFModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/ENFModule.kt @@ -13,6 +13,8 @@ import de.rki.coronawarnapp.nearby.modules.locationless.DefaultScanningSupport import de.rki.coronawarnapp.nearby.modules.locationless.ScanningSupport import de.rki.coronawarnapp.nearby.modules.tracing.DefaultTracingStatus import de.rki.coronawarnapp.nearby.modules.tracing.TracingStatus +import de.rki.coronawarnapp.nearby.modules.version.DefaultENFVersion +import de.rki.coronawarnapp.nearby.modules.version.ENFVersion import de.rki.coronawarnapp.util.di.AppContext import javax.inject.Singleton @@ -43,4 +45,8 @@ class ENFModule { @Provides fun calculationTracker(exposureDetectionTracker: DefaultExposureDetectionTracker): ExposureDetectionTracker = exposureDetectionTracker + + @Singleton + @Provides + fun enfClientVersion(enfVersion: DefaultENFVersion): ENFVersion = enfVersion } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/InternalExposureNotificationClient.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/InternalExposureNotificationClient.kt index 53c0aa60066a0dc91d8212d7d03d93bfda8faeb3..c4a058ebb575b551518825f5e57b73209cefa2e2 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/InternalExposureNotificationClient.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/InternalExposureNotificationClient.kt @@ -89,15 +89,6 @@ object InternalExposureNotificationClient { } } - suspend fun getVersion(): Long = suspendCoroutine { cont -> - exposureNotificationClient.version - .addOnSuccessListener { - cont.resume(it) - }.addOnFailureListener { - cont.resumeWithException(it) - } - } - /** * Retrieves key history from the data store on the device for uploading to your * internet-accessible server. Calling this method prompts Google Play services to display diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProvider.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProvider.kt index 59114d58d4c0bab4709d0568ffb39c052923913d..de6da660e6c4b17ee4c9bf282983b67d7249b632 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProvider.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProvider.kt @@ -6,7 +6,7 @@ import com.google.android.gms.common.api.ApiException import com.google.android.gms.nearby.exposurenotification.ExposureConfiguration import com.google.android.gms.nearby.exposurenotification.ExposureNotificationClient import de.rki.coronawarnapp.exception.reporting.ReportingConstants -import de.rki.coronawarnapp.util.GoogleAPIVersion +import de.rki.coronawarnapp.nearby.modules.version.ENFVersion import timber.log.Timber import java.io.File import javax.inject.Inject @@ -17,7 +17,7 @@ import kotlin.coroutines.suspendCoroutine @Singleton class DefaultDiagnosisKeyProvider @Inject constructor( - private val googleAPIVersion: GoogleAPIVersion, + private val enfVersion: ENFVersion, private val submissionQuota: SubmissionQuota, private val enfClient: ExposureNotificationClient ) : DiagnosisKeyProvider { @@ -40,7 +40,7 @@ class DefaultDiagnosisKeyProvider @Inject constructor( configuration } - if (googleAPIVersion.isAtLeast(GoogleAPIVersion.V16)) { + if (enfVersion.isAtLeast(ENFVersion.V16)) { provideKeys(keyFiles, usedConfiguration, token) } else { provideKeysLegacy(keyFiles, usedConfiguration, token) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersion.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersion.kt new file mode 100644 index 0000000000000000000000000000000000000000..f1983ba68c6f080becbb1d746b66ea137e1d60f7 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersion.kt @@ -0,0 +1,53 @@ +package de.rki.coronawarnapp.nearby.modules.version + +import com.google.android.gms.common.api.ApiException +import com.google.android.gms.common.api.CommonStatusCodes +import com.google.android.gms.nearby.exposurenotification.ExposureNotificationClient +import timber.log.Timber +import javax.inject.Inject +import javax.inject.Singleton +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine +import kotlin.math.abs + +@Singleton +class DefaultENFVersion @Inject constructor( + private val client: ExposureNotificationClient +) : ENFVersion { + + override suspend fun getENFClientVersion(): Long? = try { + internalGetENFClientVersion() + } catch (e: Exception) { + Timber.w(e, "Failed to get ENFClient version.") + null + } + + override suspend fun isAtLeast(compareVersion: Long): Boolean { + if (!compareVersion.isCorrectVersionLength) throw IllegalArgumentException("given version has incorrect length") + + return try { + internalGetENFClientVersion() >= compareVersion + } catch (apiException: ApiException) { + if (apiException.statusCode != CommonStatusCodes.API_NOT_CONNECTED) { + throw apiException + } else { + return false + } + } + } + + private suspend fun internalGetENFClientVersion(): Long = suspendCoroutine { cont -> + client.version + .addOnSuccessListener { cont.resume(it) } + .addOnFailureListener { cont.resumeWithException(it) } + } + + // check if a raw long has the correct length to be considered an API version + private val Long.isCorrectVersionLength + get(): Boolean = abs(this).toString().length == GOOGLE_API_VERSION_FIELD_LENGTH + + companion object { + private const val GOOGLE_API_VERSION_FIELD_LENGTH = 8 + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/ENFVersion.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/ENFVersion.kt new file mode 100644 index 0000000000000000000000000000000000000000..e0f3fec558f0a5771f0e13d2852d17f0935be3c1 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/ENFVersion.kt @@ -0,0 +1,16 @@ +package de.rki.coronawarnapp.nearby.modules.version + +interface ENFVersion { + suspend fun getENFClientVersion(): Long? + + /** + * Indicates if the client runs above a certain version + * + * @return isAboveVersion, if connected to an old unsupported version, return false + */ + suspend fun isAtLeast(compareVersion: Long): Boolean + + companion object { + const val V16 = 16000000L + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationFragment.kt index e6b1683ce430f4073e569959a96785f79ae6eec2..caac006b32562d2d7ccffa0881ad81d6eb3eec9a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationFragment.kt @@ -1,27 +1,58 @@ package de.rki.coronawarnapp.ui.information +import android.content.Intent import android.os.Bundle import android.view.View import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityNodeInfo import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController +import com.google.android.gms.nearby.exposurenotification.ExposureNotificationClient import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentInformationBinding import de.rki.coronawarnapp.ui.doNavigate import de.rki.coronawarnapp.ui.main.MainActivity import de.rki.coronawarnapp.util.ExternalActionHelper +import de.rki.coronawarnapp.util.di.AutoInject +import de.rki.coronawarnapp.util.ui.observe2 +import de.rki.coronawarnapp.util.ui.setGone import de.rki.coronawarnapp.util.ui.viewBindingLazy +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider +import de.rki.coronawarnapp.util.viewmodel.cwaViewModels +import timber.log.Timber +import javax.inject.Inject /** * Basic Fragment which links to static and web content. */ -class InformationFragment : Fragment(R.layout.fragment_information) { +class InformationFragment : Fragment(R.layout.fragment_information), AutoInject { + + @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory + private val vm: InformationFragmentViewModel by cwaViewModels { viewModelFactory } private val binding: FragmentInformationBinding by viewBindingLazy() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + + vm.currentENFVersion.observe2(this) { + binding.informationEnfVersion.apply { + setGone(it == null) + text = it + } + } + vm.appVersion.observe2(this) { + binding.informationVersion.text = it + } + + binding.informationEnfVersion.setOnClickListener { + try { + startActivity(Intent(ExposureNotificationClient.ACTION_EXPOSURE_NOTIFICATION_SETTINGS)) + } catch (e: Exception) { + Timber.e(e, "Can't open ENF settings.") + } + } + setButtonOnClickListener() setAccessibilityDelegate() } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationFragmentModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationFragmentModule.kt new file mode 100644 index 0000000000000000000000000000000000000000..7b7473491c6c067a89555f75535491ffbdd81b27 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationFragmentModule.kt @@ -0,0 +1,22 @@ +package de.rki.coronawarnapp.ui.information + +import dagger.Binds +import dagger.Module +import dagger.android.ContributesAndroidInjector +import dagger.multibindings.IntoMap +import de.rki.coronawarnapp.util.viewmodel.CWAViewModel +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelKey + +@Module +abstract class InformationFragmentModule { + @Binds + @IntoMap + @CWAViewModelKey(InformationFragmentViewModel::class) + abstract fun informationFragmentViewModel( + factory: InformationFragmentViewModel.Factory + ): CWAViewModelFactory<out CWAViewModel> + + @ContributesAndroidInjector + abstract fun informationFragment(): InformationFragment +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationFragmentViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..27f53ae57b79ddf2ea0c09a62fed833f2125a3b4 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/information/InformationFragmentViewModel.kt @@ -0,0 +1,34 @@ +package de.rki.coronawarnapp.ui.information + +import android.content.Context +import androidx.lifecycle.asLiveData +import com.squareup.inject.assisted.AssistedInject +import de.rki.coronawarnapp.BuildConfig +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.nearby.ENFClient +import de.rki.coronawarnapp.util.coroutine.DispatcherProvider +import de.rki.coronawarnapp.util.di.AppContext +import de.rki.coronawarnapp.util.viewmodel.CWAViewModel +import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOf + +class InformationFragmentViewModel @AssistedInject constructor( + dispatcherProvider: DispatcherProvider, + enfClient: ENFClient, + @AppContext private val context: Context +) : CWAViewModel(dispatcherProvider = dispatcherProvider) { + + val currentENFVersion = flow { + val enfVersion = enfClient.getENFClientVersion() + ?.let { "ENF ${context.getString(R.string.information_version).format(it)}" } + emit(enfVersion) + }.asLiveData(context = dispatcherProvider.Default) + + val appVersion = flowOf( + context.getString(R.string.information_version).format(BuildConfig.VERSION_NAME) + ).asLiveData(context = dispatcherProvider.Default) + + @AssistedInject.Factory + interface Factory : SimpleCWAViewModelFactory<InformationFragmentViewModel> +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityModule.kt index 7a596bfc0666c156fe94d05749be586282b531e7..998c4e89dd36104eba97220c65164608a4961f36 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityModule.kt @@ -4,6 +4,7 @@ import dagger.Binds import dagger.Module import dagger.android.ContributesAndroidInjector import dagger.multibindings.IntoMap +import de.rki.coronawarnapp.ui.information.InformationFragmentModule import de.rki.coronawarnapp.ui.interoperability.InteroperabilityConfigurationFragment import de.rki.coronawarnapp.ui.interoperability.InteroperabilityConfigurationFragmentModule import de.rki.coronawarnapp.ui.main.home.HomeFragmentModule @@ -23,7 +24,8 @@ import de.rki.coronawarnapp.util.viewmodel.CWAViewModelKey HomeFragmentModule::class, RiskDetailsFragmentModule::class, SettingFragmentsModule::class, - SubmissionFragmentModule::class + SubmissionFragmentModule::class, + InformationFragmentModule::class ] ) abstract class MainActivityModule { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/GoogleAPIVersion.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/GoogleAPIVersion.kt deleted file mode 100644 index 6af00068b370a93acc562d74cf16b23d8819617c..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/GoogleAPIVersion.kt +++ /dev/null @@ -1,40 +0,0 @@ -package de.rki.coronawarnapp.util - -import com.google.android.gms.common.api.ApiException -import com.google.android.gms.common.api.CommonStatusCodes -import dagger.Reusable -import de.rki.coronawarnapp.nearby.InternalExposureNotificationClient -import javax.inject.Inject -import kotlin.math.abs - -@Reusable -class GoogleAPIVersion @Inject constructor() { - /** - * Indicates if the client runs above a certain version - * - * @return isAboveVersion, if connected to an old unsupported version, return false - */ - suspend fun isAtLeast(compareVersion: Long): Boolean { - if (!compareVersion.isCorrectVersionLength) { - throw IllegalArgumentException("given version has incorrect length") - } - return try { - val currentVersion = InternalExposureNotificationClient.getVersion() - currentVersion >= compareVersion - } catch (apiException: ApiException) { - if (apiException.statusCode != CommonStatusCodes.API_NOT_CONNECTED) { - throw apiException - } - return false - } - } - - // check if a raw long has the correct length to be considered an API version - private val Long.isCorrectVersionLength - get(): Boolean = abs(this).toString().length == GOOGLE_API_VERSION_FIELD_LENGTH - - companion object { - private const val GOOGLE_API_VERSION_FIELD_LENGTH = 8 - const val V16 = 16000000L - } -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterInformationHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterInformationHelper.kt deleted file mode 100644 index 913424b0671ddfef1a1a9a00e79304ab5b6486f7..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterInformationHelper.kt +++ /dev/null @@ -1,13 +0,0 @@ -@file:JvmName("FormatterInformationHelper") - -package de.rki.coronawarnapp.util.formatter - -import de.rki.coronawarnapp.BuildConfig -import de.rki.coronawarnapp.CoronaWarnApplication -import de.rki.coronawarnapp.R - -fun formatVersion(): String { - val appContext = CoronaWarnApplication.getAppContext() - val versionName: String = BuildConfig.VERSION_NAME - return appContext.getString(R.string.information_version).format(versionName) -} diff --git a/Corona-Warn-App/src/main/res/layout/fragment_information.xml b/Corona-Warn-App/src/main/res/layout/fragment_information.xml index d51c313a9172fd2dba61cbbcdfa0a807b3e55a57..b18582525731b852608527024c79dc44b8c39503 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_information.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_information.xml @@ -1,12 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto"> - - <data> - - <import type="de.rki.coronawarnapp.util.formatter.FormatterInformationHelper" /> - - </data> + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/information_container" @@ -117,11 +112,27 @@ android:layout_height="wrap_content" android:layout_marginTop="@dimen/spacing_small" android:focusable="true" - android:text="@{FormatterInformationHelper.formatVersion()}" + tools:text="v1.8.0-RC1" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@+id/guideline_body" app:layout_constraintTop_toBottomOf="@+id/information_legal" /> + <TextView + android:id="@+id/information_enf_version" + style="@style/body2Medium" + android:visibility="gone" + tools:visibility="visible" + android:layout_width="@dimen/match_constraint" + android:paddingTop="@dimen/spacing_tiny" + android:paddingBottom="@dimen/spacing_tiny" + android:layout_height="wrap_content" + android:focusable="true" + android:background="?selectableItemBackground" + tools:text="16000000" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="@+id/guideline_body" + app:layout_constraintTop_toBottomOf="@+id/information_version" /> + <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline_body" android:layout_width="wrap_content" diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/ENFClientTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/ENFClientTest.kt index 4880b66aa8e79d52d909cc7e2ecdc9fc47a5ea96..d2bace15d737bbcfb41b3b37f6a444ad4d5b6a46 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/ENFClientTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/ENFClientTest.kt @@ -7,12 +7,14 @@ import de.rki.coronawarnapp.nearby.modules.detectiontracker.TrackedExposureDetec import de.rki.coronawarnapp.nearby.modules.diagnosiskeyprovider.DiagnosisKeyProvider import de.rki.coronawarnapp.nearby.modules.locationless.ScanningSupport import de.rki.coronawarnapp.nearby.modules.tracing.TracingStatus +import de.rki.coronawarnapp.nearby.modules.version.ENFVersion import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations import io.mockk.Runs import io.mockk.clearAllMocks import io.mockk.coEvery import io.mockk.coVerify +import io.mockk.coVerifySequence import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.just @@ -38,6 +40,7 @@ class ENFClientTest : BaseTest() { @MockK lateinit var tracingStatus: TracingStatus @MockK lateinit var scanningSupport: ScanningSupport @MockK lateinit var exposureDetectionTracker: ExposureDetectionTracker + @MockK lateinit var enfVersion: ENFVersion @BeforeEach fun setup() { @@ -56,7 +59,8 @@ class ENFClientTest : BaseTest() { diagnosisKeyProvider = diagnosisKeyProvider, tracingStatus = tracingStatus, scanningSupport = scanningSupport, - exposureDetectionTracker = exposureDetectionTracker + exposureDetectionTracker = exposureDetectionTracker, + enfVersion = enfVersion ) @Test @@ -265,4 +269,13 @@ class ENFClientTest : BaseTest() { createClient().lastSuccessfulTrackedExposureDetection().first()!!.identifier shouldBe "1" } } + + @Test + fun `enf version check is forwaded to the right module`() = runBlocking { + coEvery { enfVersion.getENFClientVersion() } returns Long.MAX_VALUE + + createClient().getENFClientVersion() shouldBe Long.MAX_VALUE + + coVerifySequence { enfVersion.getENFClientVersion() } + } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProviderTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProviderTest.kt index 24fbd423acacfc4c3ba860da038d9a9b4d447e6a..034341e7d63551c2b7aa291ea75f25cb00130f9c 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProviderTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProviderTest.kt @@ -4,7 +4,7 @@ package de.rki.coronawarnapp.nearby.modules.diagnosiskeyprovider import com.google.android.gms.nearby.exposurenotification.ExposureConfiguration import com.google.android.gms.nearby.exposurenotification.ExposureNotificationClient -import de.rki.coronawarnapp.util.GoogleAPIVersion +import de.rki.coronawarnapp.nearby.modules.version.ENFVersion import io.mockk.MockKAnnotations import io.mockk.clearAllMocks import io.mockk.coEvery @@ -19,17 +19,11 @@ import testhelpers.gms.MockGMSTask import java.io.File class DefaultDiagnosisKeyProviderTest : BaseTest() { - @MockK - lateinit var googleENFClient: ExposureNotificationClient + @MockK lateinit var googleENFClient: ExposureNotificationClient + @MockK lateinit var enfVersion: ENFVersion + @MockK lateinit var submissionQuota: SubmissionQuota + @MockK lateinit var exampleConfiguration: ExposureConfiguration - @MockK - lateinit var googleAPIVersion: GoogleAPIVersion - - @MockK - lateinit var submissionQuota: SubmissionQuota - - @MockK - lateinit var exampleConfiguration: ExposureConfiguration private val exampleKeyFiles = listOf(File("file1"), File("file2")) private val exampleToken = "123e4567-e89b-12d3-a456-426655440000" @@ -47,7 +41,7 @@ class DefaultDiagnosisKeyProviderTest : BaseTest() { ) } returns MockGMSTask.forValue(null) - coEvery { googleAPIVersion.isAtLeast(GoogleAPIVersion.V16) } returns true + coEvery { enfVersion.isAtLeast(ENFVersion.V16) } returns true } @AfterEach @@ -56,14 +50,14 @@ class DefaultDiagnosisKeyProviderTest : BaseTest() { } private fun createProvider() = DefaultDiagnosisKeyProvider( - googleAPIVersion = googleAPIVersion, + enfVersion = enfVersion, submissionQuota = submissionQuota, enfClient = googleENFClient ) @Test fun `legacy key provision is used on older ENF versions`() { - coEvery { googleAPIVersion.isAtLeast(GoogleAPIVersion.V16) } returns false + coEvery { enfVersion.isAtLeast(ENFVersion.V16) } returns false val provider = createProvider() @@ -90,7 +84,7 @@ class DefaultDiagnosisKeyProviderTest : BaseTest() { @Test fun `normal key provision is used on newer ENF versions`() { - coEvery { googleAPIVersion.isAtLeast(GoogleAPIVersion.V16) } returns true + coEvery { enfVersion.isAtLeast(ENFVersion.V16) } returns true val provider = createProvider() @@ -109,7 +103,7 @@ class DefaultDiagnosisKeyProviderTest : BaseTest() { @Test fun `passing an a null configuration leads to constructing a fallback from defaults`() { - coEvery { googleAPIVersion.isAtLeast(GoogleAPIVersion.V16) } returns true + coEvery { enfVersion.isAtLeast(ENFVersion.V16) } returns true val provider = createProvider() val fallback = ExposureConfiguration.ExposureConfigurationBuilder().build() @@ -126,7 +120,7 @@ class DefaultDiagnosisKeyProviderTest : BaseTest() { @Test fun `passing an a null configuration leads to constructing a fallback from defaults, legacy`() { - coEvery { googleAPIVersion.isAtLeast(GoogleAPIVersion.V16) } returns false + coEvery { enfVersion.isAtLeast(ENFVersion.V16) } returns false val provider = createProvider() val fallback = ExposureConfiguration.ExposureConfigurationBuilder().build() @@ -148,7 +142,7 @@ class DefaultDiagnosisKeyProviderTest : BaseTest() { @Test fun `quota is consumed silenently`() { - coEvery { googleAPIVersion.isAtLeast(GoogleAPIVersion.V16) } returns true + coEvery { enfVersion.isAtLeast(ENFVersion.V16) } returns true coEvery { submissionQuota.consumeQuota(any()) } returns false val provider = createProvider() @@ -168,7 +162,7 @@ class DefaultDiagnosisKeyProviderTest : BaseTest() { @Test fun `quota is consumed silently, legacy`() { - coEvery { googleAPIVersion.isAtLeast(GoogleAPIVersion.V16) } returns false + coEvery { enfVersion.isAtLeast(ENFVersion.V16) } returns false coEvery { submissionQuota.consumeQuota(any()) } returns false val provider = createProvider() diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersionTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersionTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..0a8ebf8773183208c180051eb08dff547f9994d3 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersionTest.kt @@ -0,0 +1,77 @@ +package de.rki.coronawarnapp.nearby.modules.version + +import com.google.android.gms.common.api.ApiException +import com.google.android.gms.common.api.CommonStatusCodes.API_NOT_CONNECTED +import com.google.android.gms.common.api.Status +import com.google.android.gms.nearby.exposurenotification.ExposureNotificationClient +import io.kotest.matchers.shouldBe +import io.mockk.Called +import io.mockk.MockKAnnotations +import io.mockk.clearAllMocks +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.verify +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runBlockingTest +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import testhelpers.gms.MockGMSTask + +@ExperimentalCoroutinesApi +internal class DefaultENFVersionTest { + + @MockK lateinit var client: ExposureNotificationClient + + @BeforeEach + fun setUp() { + MockKAnnotations.init(this) + } + + @AfterEach + fun tearDown() { + clearAllMocks() + } + + fun createInstance() = DefaultENFVersion( + client = client + ) + + @Test + fun `isAbove API v16 is true for v17`() { + every { client.version } returns MockGMSTask.forValue(17000000L) + + runBlockingTest { + createInstance().isAtLeast(ENFVersion.V16) shouldBe true + } + } + + @Test + fun `isAbove API v16 is false for v15`() { + every { client.version } returns MockGMSTask.forValue(15000000L) + + runBlockingTest { + createInstance().isAtLeast(ENFVersion.V16) shouldBe false + } + } + + @Test + fun `isAbove API v16 throws IllegalArgument for invalid version`() { + assertThrows<IllegalArgumentException> { + runBlockingTest { + createInstance().isAtLeast(1L) + } + verify { client.version wasNot Called } + } + } + + @Test + fun `isAbove API v16 false when APIException for too low version`() { + every { client.version } returns MockGMSTask.forError(ApiException(Status(API_NOT_CONNECTED))) + + runBlockingTest { + createInstance().isAtLeast(ENFVersion.V16) shouldBe false + } + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/GoogleAPIVersionTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/GoogleAPIVersionTest.kt deleted file mode 100644 index 110d8cbeed2aef3b469c0355c3a848025f1faadf..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/GoogleAPIVersionTest.kt +++ /dev/null @@ -1,75 +0,0 @@ -package de.rki.coronawarnapp.util - -import com.google.android.gms.common.api.ApiException -import com.google.android.gms.common.api.CommonStatusCodes.API_NOT_CONNECTED -import com.google.android.gms.common.api.Status -import de.rki.coronawarnapp.nearby.InternalExposureNotificationClient -import io.kotest.matchers.shouldBe -import io.mockk.Called -import io.mockk.coEvery -import io.mockk.coVerify -import io.mockk.mockkObject -import io.mockk.unmockkObject -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.runBlockingTest -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows - -@ExperimentalCoroutinesApi -internal class GoogleAPIVersionTest { - - private lateinit var classUnderTest: GoogleAPIVersion - - @BeforeEach - fun setUp() { - mockkObject(InternalExposureNotificationClient) - classUnderTest = GoogleAPIVersion() - } - - @AfterEach - fun tearDown() { - unmockkObject(InternalExposureNotificationClient) - } - - @Test - fun `isAbove API v16 is true for v17`() { - coEvery { InternalExposureNotificationClient.getVersion() } returns 17000000L - - runBlockingTest { - classUnderTest.isAtLeast(GoogleAPIVersion.V16) shouldBe true - } - } - - @Test - fun `isAbove API v16 is false for v15`() { - coEvery { InternalExposureNotificationClient.getVersion() } returns 15000000L - - runBlockingTest { - classUnderTest.isAtLeast(GoogleAPIVersion.V16) shouldBe false - } - } - - @Test - fun `isAbove API v16 throws IllegalArgument for invalid version`() { - assertThrows<IllegalArgumentException> { - runBlockingTest { - classUnderTest.isAtLeast(1L) - } - coVerify { - InternalExposureNotificationClient.getVersion() wasNot Called - } - } - } - - @Test - fun `isAbove API v16 false when APIException for too low version`() { - coEvery { InternalExposureNotificationClient.getVersion() } throws - ApiException(Status(API_NOT_CONNECTED)) - - runBlockingTest { - classUnderTest.isAtLeast(GoogleAPIVersion.V16) shouldBe false - } - } -} diff --git a/Corona-Warn-App/src/test/java/testhelpers/gms/MockGMSTask.kt b/Corona-Warn-App/src/test/java/testhelpers/gms/MockGMSTask.kt index d0ed11f4ec929f0ba4ec6ff7e89bfc832fef9f01..321a733ae210c6fa0dd2600912032fdbb4d08159 100644 --- a/Corona-Warn-App/src/test/java/testhelpers/gms/MockGMSTask.kt +++ b/Corona-Warn-App/src/test/java/testhelpers/gms/MockGMSTask.kt @@ -8,12 +8,12 @@ import io.mockk.mockk object MockGMSTask { fun <T> forError(error: Exception): Task<T> = mockk<Task<T>>().apply { - every { addOnSuccessListener(any()) } answers { + every { addOnFailureListener(any()) } answers { val listener = arg<OnFailureListener>(0) listener.onFailure(error) this@apply } - every { addOnFailureListener(any()) } returns this + every { addOnSuccessListener(any()) } returns this } fun <T> forValue(value: T): Task<T> = mockk<Task<T>>().apply {