From f14311afda89dccd80ee74bac5e665808b78cd63 Mon Sep 17 00:00:00 2001
From: Matthias Urhahn <matthias.urhahn@sap.com>
Date: Mon, 3 May 2021 15:59:11 +0200
Subject: [PATCH] Fix flaky unit test. (#3027)

Co-authored-by: harambasicluka <64483219+harambasicluka@users.noreply.github.com>
Co-authored-by: Juraj Kusnier <jurajkusnier@users.noreply.github.com>
---
 .reuse/dep5                                   |  4 ++
 .../debugoptions/ui/DebugOptionsFragment.kt   |  2 +-
 .../ui/DebugOptionsFragmentViewModel.kt       | 18 +++---
 .../coronawarnapp/util/ui/SmartLiveData.kt    | 57 -------------------
 ...taTestExtension.kt => LiveDataTestUtil.kt} | 18 +++++-
 .../ui/DebugOptionsFragmentViewModelTest.kt   | 43 +++-----------
 6 files changed, 38 insertions(+), 104 deletions(-)
 delete mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/SmartLiveData.kt
 rename Corona-Warn-App/src/test/java/testhelpers/extensions/{LiveDataTestExtension.kt => LiveDataTestUtil.kt} (73%)

diff --git a/.reuse/dep5 b/.reuse/dep5
index ae4ac319f..25db19164 100644
--- a/.reuse/dep5
+++ b/.reuse/dep5
@@ -54,4 +54,8 @@ License: Apache-2.0
 
 Files: Corona-Warn-App/src/main/res/font/roboto.ttf
 Copyright: 2011 Google Inc.
+License: Apache-2.0
+
+Files: Corona-Warn-App/src/test/java/testhelpers/extensions/LiveDataTestUtil.kt
+Copyright: 2019 The Android Open Source Project
 License: Apache-2.0
\ No newline at end of file
diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragment.kt
index 638755b2c..b0e02bfce 100644
--- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragment.kt
+++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragment.kt
@@ -75,7 +75,7 @@ class DebugOptionsFragment : Fragment(R.layout.fragment_test_debugoptions), Auto
                 environmentPubkeyAppconfig.text = "AppConfigPubKey:\n${state.pubKeyAppConfig}"
             }
         }
-        vm.environmentChangeEvent.observe2(this) {
+        vm.environmentStateChange.observe2(this) {
             showSnackBar("Environment changed to: $it\nForce stop & restart the app!")
         }
     }
diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModel.kt
index 9d959a1db..f7f5f5d48 100644
--- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModel.kt
+++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModel.kt
@@ -1,5 +1,6 @@
 package de.rki.coronawarnapp.test.debugoptions.ui
 
+import androidx.lifecycle.asLiveData
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import de.rki.coronawarnapp.environment.EnvironmentSetup
@@ -7,25 +8,24 @@ import de.rki.coronawarnapp.environment.EnvironmentSetup.Type.Companion.toEnviro
 import de.rki.coronawarnapp.test.debugoptions.ui.EnvironmentState.Companion.toEnvironmentState
 import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
 import de.rki.coronawarnapp.util.ui.SingleLiveEvent
-import de.rki.coronawarnapp.util.ui.smartLiveData
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
 import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
+import kotlinx.coroutines.flow.MutableStateFlow
 
 class DebugOptionsFragmentViewModel @AssistedInject constructor(
     private val envSetup: EnvironmentSetup,
     dispatcherProvider: DispatcherProvider
 ) : CWAViewModel(dispatcherProvider = dispatcherProvider) {
 
-    val environmentState by smartLiveData {
-        envSetup.toEnvironmentState()
-    }
-    val environmentChangeEvent = SingleLiveEvent<EnvironmentSetup.Type>()
+    private val environmentStateFlow = MutableStateFlow(envSetup.toEnvironmentState())
+    val environmentState = environmentStateFlow.asLiveData(context = dispatcherProvider.Default)
+    val environmentStateChange = SingleLiveEvent<EnvironmentState>()
 
     fun selectEnvironmentTytpe(type: String) {
-        environmentState.update {
-            envSetup.currentEnvironment = type.toEnvironmentType()
-            environmentChangeEvent.postValue(envSetup.currentEnvironment)
-            envSetup.toEnvironmentState()
+        envSetup.currentEnvironment = type.toEnvironmentType()
+        envSetup.toEnvironmentState().let {
+            environmentStateFlow.value = it
+            environmentStateChange.postValue(it)
         }
     }
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/SmartLiveData.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/SmartLiveData.kt
deleted file mode 100644
index d598cdf5b..000000000
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ui/SmartLiveData.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-package de.rki.coronawarnapp.util.ui
-
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import kotlin.properties.ReadOnlyProperty
-import kotlin.reflect.KProperty
-
-fun <T : Any> ViewModel.smartLiveData(
-    dispatcher: CoroutineDispatcher = Dispatchers.Default,
-    liveDataFactory: (ViewModel, CoroutineDispatcher) -> SmartLiveData<T> = { vm, disp ->
-        SmartLiveData(vm, disp)
-    },
-    initAction: suspend () -> T
-) = SmartLiveDataProperty(dispatcher, initAction, liveDataFactory)
-
-class SmartLiveDataProperty<T : Any, LV : SmartLiveData<T>>(
-    private val dispatcher: CoroutineDispatcher = Dispatchers.Default,
-    private val initialValueProvider: suspend () -> T,
-    private val liveDataFactory: (ViewModel, CoroutineDispatcher) -> LV
-) : ReadOnlyProperty<ViewModel, SmartLiveData<T>> {
-
-    private var liveData: SmartLiveData<T>? = null
-
-    override fun getValue(
-        thisRef: ViewModel,
-        property: KProperty<*>
-    ): SmartLiveData<T> {
-        liveData?.let {
-            return@getValue it
-        }
-
-        return liveDataFactory(thisRef, dispatcher).also {
-            liveData = it
-            thisRef.viewModelScope.launch(context = dispatcher) {
-                it.postValue(initialValueProvider())
-            }
-        }
-    }
-}
-
-open class SmartLiveData<T : Any>(
-    private val viewModel: ViewModel,
-    private val dispatcher: CoroutineDispatcher
-) : MutableLiveData<T>() {
-
-    fun update(updateAction: (T) -> T) {
-        observeOnce {
-            viewModel.viewModelScope.launch(context = dispatcher) {
-                postValue(updateAction(it))
-            }
-        }
-    }
-}
diff --git a/Corona-Warn-App/src/test/java/testhelpers/extensions/LiveDataTestExtension.kt b/Corona-Warn-App/src/test/java/testhelpers/extensions/LiveDataTestUtil.kt
similarity index 73%
rename from Corona-Warn-App/src/test/java/testhelpers/extensions/LiveDataTestExtension.kt
rename to Corona-Warn-App/src/test/java/testhelpers/extensions/LiveDataTestUtil.kt
index 1041e72dd..80af61128 100644
--- a/Corona-Warn-App/src/test/java/testhelpers/extensions/LiveDataTestExtension.kt
+++ b/Corona-Warn-App/src/test/java/testhelpers/extensions/LiveDataTestUtil.kt
@@ -1,9 +1,25 @@
-package testhelpers.extensions
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 /**
  * Thanks to https://github.com/android/architecture-components-samples/blob/master/LiveDataSample/app/src/test/java/com/android/example/livedatabuilder/util/LiveDataTestUtil.kt
  */
 
+package testhelpers.extensions
+
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.Observer
 import java.util.concurrent.CountDownLatch
diff --git a/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModelTest.kt b/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModelTest.kt
index bc5ae9e45..c1b802516 100644
--- a/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModelTest.kt
+++ b/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModelTest.kt
@@ -1,25 +1,19 @@
 package de.rki.coronawarnapp.test.debugoptions.ui
 
-import androidx.lifecycle.Observer
 import de.rki.coronawarnapp.environment.EnvironmentSetup
 import io.kotest.matchers.shouldBe
 import io.mockk.MockKAnnotations
-import io.mockk.Runs
 import io.mockk.every
 import io.mockk.impl.annotations.MockK
-import io.mockk.just
-import io.mockk.mockk
-import io.mockk.verify
 import org.junit.jupiter.api.BeforeEach
 import org.junit.jupiter.api.Test
 import org.junit.jupiter.api.extension.ExtendWith
 import testhelpers.BaseTestInstrumentation
 import testhelpers.TestDispatcherProvider
-import testhelpers.extensions.CoroutinesTestExtension
 import testhelpers.extensions.InstantExecutorExtension
-import testhelpers.flakyTest
+import testhelpers.extensions.getOrAwaitValue
 
-@ExtendWith(InstantExecutorExtension::class, CoroutinesTestExtension::class)
+@ExtendWith(InstantExecutorExtension::class)
 class DebugOptionsFragmentViewModelTest : BaseTestInstrumentation() {
 
     @MockK private lateinit var environmentSetup: EnvironmentSetup
@@ -54,38 +48,15 @@ class DebugOptionsFragmentViewModelTest : BaseTestInstrumentation() {
     )
 
     @Test
-    fun `toggeling the env works`() = flakyTest {
+    fun `toggeling the env works`() {
         currentEnvironment = EnvironmentSetup.Type.DEV
         val vm = createViewModel()
-
-        val states = mutableListOf<EnvironmentState>()
-        val observerState = mockk<Observer<EnvironmentState>>()
-        every { observerState.onChanged(capture(states)) } just Runs
-        vm.environmentState.observeForever(observerState)
-
-        val events = mutableListOf<EnvironmentSetup.Type>()
-        val observerEvent = mockk<Observer<EnvironmentSetup.Type>>()
-        every { observerEvent.onChanged(capture(events)) } just Runs
-        vm.environmentChangeEvent.observeForever(observerEvent)
+        vm.environmentState.getOrAwaitValue().current shouldBe EnvironmentSetup.Type.DEV
 
         vm.selectEnvironmentTytpe(EnvironmentSetup.Type.DEV.rawKey)
-        vm.selectEnvironmentTytpe(EnvironmentSetup.Type.WRU_XA.rawKey)
-
-        verify(exactly = 3, timeout = 3000) { observerState.onChanged(any()) }
-        verify(exactly = 2, timeout = 3000) { observerEvent.onChanged(any()) }
-
-        states[0].apply {
-            current shouldBe EnvironmentSetup.Type.DEV
-        }
-
-        states[1].apply {
-            current shouldBe EnvironmentSetup.Type.DEV
-        }
-        events[0] shouldBe EnvironmentSetup.Type.DEV
+        vm.environmentState.getOrAwaitValue().current shouldBe EnvironmentSetup.Type.DEV
 
-        states[2].apply {
-            current shouldBe EnvironmentSetup.Type.WRU_XA
-        }
-        events[1] shouldBe EnvironmentSetup.Type.WRU_XA
+        vm.selectEnvironmentTytpe(EnvironmentSetup.Type.WRU_XA.rawKey)
+        vm.environmentState.getOrAwaitValue().current shouldBe EnvironmentSetup.Type.WRU_XA
     }
 }
-- 
GitLab