From 90ad5d36c703888c20380b311998bc8f33d1f169 Mon Sep 17 00:00:00 2001
From: Matthias Urhahn <darken@darken.eu>
Date: Thu, 17 Sep 2020 08:55:05 +0200
Subject: [PATCH] File Logger Introduction for Test Builds (EXPOSUREAPP-2640)
 (#1176)

* Align TestFragment Crash behavior with background transaction crash behavior.

* Add logging mechanism to debug hotfix issue.

* Lint Resolvement, Nav Graph Issue clean, Enable Log for deviceForTesters, Correct Quota Tests due to now lacking exception

Co-authored-by: Matthias Urhahn <matthias.urhahn@sap.com>
Co-authored-by: d067928 <jakob.moeller@sap.com>
---
 .../TestForAPIFragment.kt                     | 73 ++++++++++------
 .../TestRiskLevelCalculation.kt               |  4 +-
 .../res/layout/fragment_test_for_a_p_i.xml    | 26 +++++-
 .../coronawarnapp/CoronaWarnApplication.kt    | 49 ++++++-----
 .../util/GoogleQuotaCalculator.kt             | 41 +++++++--
 .../coronawarnapp/util/debug/FileLogger.kt    | 40 +++++++++
 .../util/debug/FileLoggerTree.kt              | 86 +++++++++++++++++++
 .../src/main/res/navigation/nav_graph.xml     |  5 ++
 .../util/GoogleQuotaCalculatorTest.kt         |  4 +-
 9 files changed, 269 insertions(+), 59 deletions(-)
 create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/debug/FileLogger.kt
 create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/debug/FileLoggerTree.kt

diff --git a/Corona-Warn-App/src/deviceForTesters/java/de.rki.coronawarnapp/TestForAPIFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de.rki.coronawarnapp/TestForAPIFragment.kt
index 85f21bbb4..49c2a8059 100644
--- a/Corona-Warn-App/src/deviceForTesters/java/de.rki.coronawarnapp/TestForAPIFragment.kt
+++ b/Corona-Warn-App/src/deviceForTesters/java/de.rki.coronawarnapp/TestForAPIFragment.kt
@@ -49,33 +49,9 @@ import de.rki.coronawarnapp.storage.tracing.TracingIntervalRepository
 import de.rki.coronawarnapp.transaction.RiskLevelTransaction
 import de.rki.coronawarnapp.ui.viewmodel.TracingViewModel
 import de.rki.coronawarnapp.util.KeyFileHelper
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_api_enter_other_keys
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_api_get_check_exposure
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_api_get_exposure_keys
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_api_scan_qr_code
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_api_share_my_keys
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_api_submit_keys
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_api_test_start
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_calculate_risk_level
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_clear_db
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_insert_exposure_summary
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_retrieve_exposure_summary
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_tracing_duration_in_retention_period
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.button_tracing_intervals
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.label_exposure_summary_attenuation
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.label_exposure_summary_daysSinceLastExposure
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.label_exposure_summary_matchedKeyCount
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.label_exposure_summary_maximumRiskScore
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.label_exposure_summary_summationRiskScore
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.label_googlePlayServices_version
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.label_latest_key_date
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.label_my_keys
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.qr_code_viewpager
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.test_api_switch_last_three_hours_from_server
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.test_api_switch_background_notifications
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.text_my_keys
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.text_scanned_key
+import kotlinx.android.synthetic.deviceForTesters.fragment_test_for_a_p_i.*
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.async
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import org.joda.time.DateTime
@@ -275,6 +251,51 @@ class TestForAPIFragment : Fragment(), InternalExposureNotificationPermissionHel
                 showToast(TimeVariables.getActiveTracingDaysInRetentionPeriod().toString())
             }
         }
+
+        binding.testLogfileToggle.isChecked = CoronaWarnApplication.fileLogger?.isLogging ?: false
+        binding.testLogfileToggle.setOnClickListener { buttonView ->
+            CoronaWarnApplication.fileLogger?.let {
+                if (binding.testLogfileToggle.isChecked) {
+                    it.start()
+                } else {
+                    it.stop()
+                }
+            }
+        }
+
+        binding.testLogfileShare.setOnClickListener {
+            CoronaWarnApplication.fileLogger?.let {
+                lifecycleScope.launch {
+                    val targetPath = withContext(Dispatchers.IO) {
+                        async {
+                            if (!it.logFile.exists()) return@async null
+
+                            val externalPath = File(
+                                requireContext().getExternalFilesDir(null),
+                                "LogFile-${System.currentTimeMillis()}.log"
+                            )
+
+                            it.logFile.copyTo(externalPath)
+
+                            return@async externalPath
+                        }
+                    }.await()
+                    if (targetPath != null) {
+                        Toast.makeText(
+                            requireActivity(),
+                            "Logfile copied to $targetPath",
+                            Toast.LENGTH_SHORT
+                        ).show()
+                    } else {
+                        Toast.makeText(
+                            requireActivity(),
+                            "No log file available",
+                            Toast.LENGTH_SHORT
+                        ).show()
+                    }
+                }
+            }
+        }
     }
 
     override fun onResume() {
diff --git a/Corona-Warn-App/src/deviceForTesters/java/de.rki.coronawarnapp/TestRiskLevelCalculation.kt b/Corona-Warn-App/src/deviceForTesters/java/de.rki.coronawarnapp/TestRiskLevelCalculation.kt
index b19611425..9128afba2 100644
--- a/Corona-Warn-App/src/deviceForTesters/java/de.rki.coronawarnapp/TestRiskLevelCalculation.kt
+++ b/Corona-Warn-App/src/deviceForTesters/java/de.rki.coronawarnapp/TestRiskLevelCalculation.kt
@@ -37,7 +37,7 @@ import de.rki.coronawarnapp.ui.viewmodel.SubmissionViewModel
 import de.rki.coronawarnapp.ui.viewmodel.TracingViewModel
 import de.rki.coronawarnapp.util.KeyFileHelper
 import de.rki.coronawarnapp.util.security.SecurityHelper
-import kotlinx.android.synthetic.deviceForTesters.fragment_test_risk_level_calculation.transmission_number
+import kotlinx.android.synthetic.deviceForTesters.fragment_test_risk_level_calculation.*
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
@@ -159,7 +159,7 @@ class TestRiskLevelCalculation : Fragment() {
         try {
             RetrieveDiagnosisKeysTransaction.start()
             calculateRiskLevel()
-        } catch (e: TransactionException) {
+        } catch (e: Exception) {
             e.report(ExceptionCategory.INTERNAL)
         }
     }
diff --git a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_for_a_p_i.xml b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_for_a_p_i.xml
index 9b13b7db1..2507759dc 100644
--- a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_for_a_p_i.xml
+++ b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_for_a_p_i.xml
@@ -1,5 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
-<layout xmlns:android="http://schemas.android.com/apk/res/android">
+<layout xmlns:tools="http://schemas.android.com/tools"
+    tools:ignore="HardcodedText"
+    xmlns:android="http://schemas.android.com/apk/res/android">
 
     <data>
 
@@ -25,6 +27,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content" />
 
+
             <Switch
                 android:id="@+id/test_api_switch_last_three_hours_from_server"
                 style="@style/body1"
@@ -41,6 +44,27 @@
                 android:text="@string/test_api_switch_background_notifications"
                 android:theme="@style/switchBase" />
 
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:orientation="horizontal"
+                android:layout_height="wrap_content">
+
+                <Switch
+                    android:id="@+id/test_logfile_toggle"
+                    style="@style/body1"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="Logfile enabled"
+                    android:theme="@style/switchBase" />
+
+                <Button
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:id="@+id/test_logfile_share"
+                    android:text="Share log" />
+
+            </LinearLayout>
+
             <TextView
                 android:id="@+id/label_exposure_summary"
                 style="@style/headline6"
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/CoronaWarnApplication.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/CoronaWarnApplication.kt
index c9814562f..21ce0efd1 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/CoronaWarnApplication.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/CoronaWarnApplication.kt
@@ -23,6 +23,7 @@ import de.rki.coronawarnapp.notification.NotificationHelper
 import de.rki.coronawarnapp.storage.LocalData
 import de.rki.coronawarnapp.transaction.RetrieveDiagnosisKeysTransaction
 import de.rki.coronawarnapp.util.ConnectivityHelper
+import de.rki.coronawarnapp.util.debug.FileLogger
 import de.rki.coronawarnapp.worker.BackgroundWorkHelper
 import de.rki.coronawarnapp.worker.BackgroundWorkScheduler
 import kotlinx.coroutines.launch
@@ -49,6 +50,7 @@ class CoronaWarnApplication : Application(), LifecycleObserver,
             instance.applicationContext
 
         const val TEN_MINUTE_TIMEOUT_IN_MS = 10 * 60 * 1000L
+        var fileLogger: FileLogger? = null
     }
 
     private lateinit var errorReceiver: ErrorReportReceiver
@@ -71,6 +73,9 @@ class CoronaWarnApplication : Application(), LifecycleObserver,
         if (BuildConfig.DEBUG) {
             Timber.plant(Timber.DebugTree())
         }
+        if ((BuildConfig.FLAVOR == "deviceForTesters" || BuildConfig.DEBUG)) {
+            fileLogger = FileLogger(this)
+        }
 
         // notification to test the WakeUpService from Google when the app
         // was force stopped
@@ -82,27 +87,9 @@ class CoronaWarnApplication : Application(), LifecycleObserver,
             ProcessLifecycleOwner.get().lifecycleScope.launch {
                 // we want a wakelock as the OS does not handle this for us like in the background
                 // job execution
-                val wakeLock: PowerManager.WakeLock =
-                    (getSystemService(Context.POWER_SERVICE) as PowerManager).run {
-                        newWakeLock(
-                            PowerManager.PARTIAL_WAKE_LOCK,
-                            TAG + "-WAKE-" + UUID.randomUUID().toString()
-                        ).apply {
-                            acquire(TEN_MINUTE_TIMEOUT_IN_MS)
-                        }
-                    }
-
+                val wakeLock = createWakeLock()
                 // we keep a wifi lock to wake up the wifi connection in case the device is dozing
-                val wifiLock: WifiManager.WifiLock =
-                    (getSystemService(Context.WIFI_SERVICE) as WifiManager).run {
-                        createWifiLock(
-                            WifiManager.WIFI_MODE_FULL_HIGH_PERF,
-                            TAG + "-WIFI-" + UUID.randomUUID().toString()
-                        ).apply {
-                            acquire()
-                        }
-                    }
-
+                val wifiLock = createWifiLock()
                 try {
                     BackgroundWorkHelper.sendDebugNotification(
                         "Automatic mode is on", "Check if we have downloaded keys already today"
@@ -129,6 +116,28 @@ class CoronaWarnApplication : Application(), LifecycleObserver,
         }
     }
 
+    private fun createWakeLock(): PowerManager.WakeLock =
+        (getSystemService(Context.POWER_SERVICE) as PowerManager)
+            .run {
+                newWakeLock(
+                    PowerManager.PARTIAL_WAKE_LOCK,
+                    TAG + "-WAKE-" + UUID.randomUUID().toString()
+                ).apply {
+                    acquire(TEN_MINUTE_TIMEOUT_IN_MS)
+                }
+            }
+
+    private fun createWifiLock(): WifiManager.WifiLock =
+        (getSystemService(Context.WIFI_SERVICE) as WifiManager)
+            .run {
+                createWifiLock(
+                    WifiManager.WIFI_MODE_FULL_HIGH_PERF,
+                    TAG + "-WIFI-" + UUID.randomUUID().toString()
+                ).apply {
+                    acquire()
+                }
+            }
+
     /**
      * Callback when the app is open but backgrounded
      */
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/GoogleQuotaCalculator.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/GoogleQuotaCalculator.kt
index e7aace190..e686eec75 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/GoogleQuotaCalculator.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/GoogleQuotaCalculator.kt
@@ -6,6 +6,7 @@ import org.joda.time.DateTime
 import org.joda.time.DateTimeZone
 import org.joda.time.Duration
 import org.joda.time.Instant
+import timber.log.Timber
 
 /**
  * This Calculator class takes multiple parameters to check if the Google API
@@ -28,34 +29,58 @@ class GoogleQuotaCalculator(
     override var hasExceededQuota: Boolean = false
 
     override fun calculateQuota(): Boolean {
-        if (Instant.now().isAfter(LocalData.nextTimeRateLimitingUnlocks)) {
+        val oldQuota = LocalData.googleAPIProvideDiagnosisKeysCallCount
+        var currentQuota = oldQuota
+
+        val now = Instant.now()
+        val nextUnlock = LocalData.nextTimeRateLimitingUnlocks
+
+        Timber.v(
+            "calculateQuota() start! (currentQuota=%s, timeNow=%s, timeReset=%s)",
+            oldQuota, now, nextUnlock
+        )
+        if (now.isAfter(nextUnlock)) {
             LocalData.nextTimeRateLimitingUnlocks = DateTime
                 .now(quotaTimeZone)
                 .withChronology(quotaChronology)
                 .plus(quotaResetPeriod)
                 .withTimeAtStartOfDay()
                 .toInstant()
-            LocalData.googleAPIProvideDiagnosisKeysCallCount = 0
+            Timber.d("calculateQuota() quota reset to 0.")
+            currentQuota = 0
+        } else {
+            Timber.v("calculateQuota() can't be reset yet.")
         }
 
-        if (LocalData.googleAPIProvideDiagnosisKeysCallCount <= quotaLimit) {
-            LocalData.googleAPIProvideDiagnosisKeysCallCount += incrementByAmount
+        if (currentQuota <= quotaLimit) {
+            currentQuota += incrementByAmount
         }
 
-        hasExceededQuota = LocalData.googleAPIProvideDiagnosisKeysCallCount > quotaLimit
+        if (currentQuota != oldQuota) {
+            LocalData.googleAPIProvideDiagnosisKeysCallCount = currentQuota
+        }
 
-        return hasExceededQuota
+        return (currentQuota > quotaLimit).also {
+            hasExceededQuota = it
+            Timber.v(
+                "calculateQuota() done! -> oldQuota=%d, currentQuotaHm=%d, quotaLimit=%d, EXCEEDED=%b",
+                oldQuota, currentQuota, quotaLimit, it
+            )
+        }
     }
 
     override fun resetProgressTowardsQuota(newProgress: Int) {
         if (newProgress > quotaLimit) {
-            throw IllegalArgumentException("cannot reset progress to a value higher than the quota limit")
+            Timber.w("cannot reset progress to a value higher than the quota limit")
+            return
         }
         if (newProgress % incrementByAmount != 0) {
-            throw IllegalArgumentException("supplied progress is no multiple of $incrementByAmount")
+            Timber.e("supplied progress is no multiple of $incrementByAmount")
+            return
         }
         LocalData.googleAPIProvideDiagnosisKeysCallCount = newProgress
         hasExceededQuota = false
+        Timber.d("resetProgressTowardsQuota(newProgress=%d) done", newProgress)
     }
 
     override fun getProgressTowardsQuota(): Int = LocalData.googleAPIProvideDiagnosisKeysCallCount
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/debug/FileLogger.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/debug/FileLogger.kt
new file mode 100644
index 000000000..eb3b29093
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/debug/FileLogger.kt
@@ -0,0 +1,40 @@
+package de.rki.coronawarnapp.util.debug
+
+import android.content.Context
+import timber.log.Timber
+import java.io.File
+
+class FileLogger constructor(private val context: Context) {
+
+    val logFile = File(context.cacheDir, "FileLoggerTree.log")
+    val triggerFile = File(context.filesDir, "FileLoggerTree.trigger")
+    private var loggerTree: FileLoggerTree? = null
+
+    val isLogging: Boolean
+        get() = loggerTree != null
+
+    init {
+        if (triggerFile.exists()) {
+            start()
+        }
+    }
+
+    fun start() {
+        if (loggerTree != null) return
+
+        loggerTree = FileLoggerTree(logFile).also {
+            Timber.plant(it)
+            it.start()
+            triggerFile.createNewFile()
+        }
+    }
+
+    fun stop() {
+        loggerTree?.let {
+            it.stop()
+            logFile.delete()
+            triggerFile.delete()
+            loggerTree = null
+        }
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/debug/FileLoggerTree.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/debug/FileLoggerTree.kt
new file mode 100644
index 000000000..84bd21ad6
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/debug/FileLoggerTree.kt
@@ -0,0 +1,86 @@
+package de.rki.coronawarnapp.util.debug
+
+import android.annotation.SuppressLint
+import android.util.Log
+import timber.log.Timber
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.OutputStreamWriter
+
+@SuppressLint("LogNotTimber")
+class FileLoggerTree(private val logFile: File) : Timber.DebugTree() {
+    private var logWriter: OutputStreamWriter? = null
+
+    @SuppressLint("SetWorldReadable")
+    @Synchronized
+    fun start() {
+        if (logWriter != null) return
+
+        logFile.parentFile.mkdirs()
+        if (logFile.createNewFile()) {
+            Log.i(TAG, "File logger writing to " + logFile.path)
+        }
+        if (logFile.setReadable(true, false)) {
+            Log.i(TAG, "Debug run log read permission set")
+        }
+
+        try {
+            logWriter = OutputStreamWriter(FileOutputStream(logFile, true))
+            logWriter!!.write("=== BEGIN ===\n")
+            logWriter!!.write("Logfile: $logFile\n")
+            logWriter!!.flush()
+            Log.i(TAG, "File logger started.")
+        } catch (e: IOException) {
+            e.printStackTrace()
+
+            logFile.delete()
+            if (logWriter != null) logWriter!!.close()
+        }
+    }
+
+    @Synchronized
+    fun stop() {
+        logWriter?.let {
+            logWriter = null
+            try {
+                it.write("=== END ===\n")
+                it.close()
+            } catch (ignore: IOException) {
+            }
+            Log.i(TAG, "File logger stopped.")
+        }
+    }
+
+    override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
+        logWriter?.let {
+            try {
+                it.write("${System.currentTimeMillis()}  ${priorityToString(priority)}/$tag: $message\n")
+                it.flush()
+            } catch (e: IOException) {
+                Timber.tag(TAG).e(e)
+                try {
+                    it.close()
+                } catch (ignore: Exception) {
+                }
+                logWriter = null
+            }
+        }
+    }
+
+    override fun toString(): String {
+        return "FileLoggerTree(file=$logFile)"
+    }
+
+    companion object {
+        private const val TAG = "FileLoggerTree"
+        private fun priorityToString(priority: Int): String = when (priority) {
+            Log.ERROR -> "E"
+            Log.WARN -> "W"
+            Log.INFO -> "I"
+            Log.DEBUG -> "D"
+            Log.VERBOSE -> "V"
+            else -> priority.toString()
+        }
+    }
+}
diff --git a/Corona-Warn-App/src/main/res/navigation/nav_graph.xml b/Corona-Warn-App/src/main/res/navigation/nav_graph.xml
index 13ffd4246..21404c33b 100644
--- a/Corona-Warn-App/src/main/res/navigation/nav_graph.xml
+++ b/Corona-Warn-App/src/main/res/navigation/nav_graph.xml
@@ -230,6 +230,11 @@
         android:name="de.rki.coronawarnapp.ui.submission.SubmissionTanFragment"
         android:label="fragment_submission_tan"
         tools:layout="@layout/fragment_submission_tan">
+        <action
+            android:id="@+id/action_submissionTanFragment_to_submissionDispatcherFragment"
+            app:destination="@id/submissionDispatcherFragment"
+            app:popUpTo="@id/submissionDispatcherFragment"
+            app:popUpToInclusive="true" />
         <action
             android:id="@+id/action_submissionTanFragment_to_submissionResultFragment"
             app:destination="@id/submissionResultFragment"
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/GoogleQuotaCalculatorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/GoogleQuotaCalculatorTest.kt
index 1a506b0cd..1ab5fa9b7 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/GoogleQuotaCalculatorTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/GoogleQuotaCalculatorTest.kt
@@ -124,7 +124,7 @@ internal class GoogleQuotaCalculatorTest {
         assertEquals(false, classUnderTest.hasExceededQuota)
     }
 
-    @Test(expected = IllegalArgumentException::class)
+    @Test
     fun `getProgressTowardsQuota is reset but the reset value is no multiple of incrementByAmount`() {
         var latestCallNumberWithoutLimiting = 1
         for (callNumber in 1..5) {
@@ -177,7 +177,7 @@ internal class GoogleQuotaCalculatorTest {
         assertEquals(newProgressAfterReset, classUnderTest.getProgressTowardsQuota())
     }
 
-    @Test(expected = IllegalArgumentException::class)
+    @Test
     fun `getProgressTowardsQuota is reset and the quota is not recalculated and the progress throws an error because of too high newProgress`() {
         var latestCallNumberWithoutLimiting = 1
         var progressBeforeReset: Int? = null
-- 
GitLab