Skip to content
This repository has been archived by the owner on Jun 20, 2023. It is now read-only.

Test Menu options for Country selection and Measurement (EXPOSUREAPP-2229) #1086

Merged
merged 24 commits into from
Sep 3, 2020
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f627704
Implement country filter (#2229)
mertsafter Aug 25, 2020
7d43226
Implement measure for Risk calculation and Key Retrieval (#2229)
mertsafter Aug 26, 2020
a3d5263
Some code cleanup for measureRiskLevelAndKeyRetrieval
mertsafter Aug 27, 2020
d7cae29
Some code cleanup (#2229)
mertsafter Aug 28, 2020
249afc5
Implement usage of AppConfig usage for country codes (#2229)
mertsafter Aug 28, 2020
6c8eff4
change CURRENT_COUNTRY val to var
mertsafter Aug 28, 2020
c941829
Suppress "LongMethod" warning (#2229)
mertsafter Aug 29, 2020
ce5ce76
Adjust unit tests to respect new country filter
mertsafter Aug 29, 2020
df93d01
Align with backend for app config country property name
mertsafter Aug 31, 2020
03a6219
Display total file size of keys in results (#2229)
mertsafter Aug 31, 2020
7b763cb
Fix linting issue
mertsafter Aug 31, 2020
38e26d9
Update strings.xml files
mertsafter Sep 1, 2020
468d09e
Do refactoring and some code clean up
mertsafter Sep 1, 2020
56745b1
Move RiskLevel and Key retrieval measurement in own class
mertsafter Sep 1, 2020
873b1d9
Move code in logical classes. Remove translation in test fragment
mertsafter Sep 2, 2020
0f6d565
Fix linting and Unit tests
mertsafter Sep 2, 2020
0409073
Remove 3hour boolean check from debug class.
d4rken Sep 2, 2020
a1a2e12
Refactor CountryWrapper and missing dates check.
d4rken Sep 2, 2020
a4c4f82
Fix linting and Unit tests
mertsafter Sep 2, 2020
085c084
Add Unit test for getMissingDates
mertsafter Sep 3, 2020
b76b418
Code cleanup
mertsafter Sep 3, 2020
daf4016
Hide keyboard on action
mertsafter Sep 3, 2020
c502491
Extend statistics for api submission
mertsafter Sep 3, 2020
3050ace
Set callback to null after invoked
mertsafter Sep 3, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.util.Base64
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.ImageView
import android.widget.Switch
import android.widget.Toast
Expand Down Expand Up @@ -46,35 +47,12 @@ import de.rki.coronawarnapp.storage.AppDatabase
import de.rki.coronawarnapp.storage.ExposureSummaryRepository
import de.rki.coronawarnapp.storage.LocalData
import de.rki.coronawarnapp.storage.tracing.TracingIntervalRepository
import de.rki.coronawarnapp.transaction.RetrieveDiagnosisKeysTransaction
import de.rki.coronawarnapp.transaction.RiskLevelTransaction
import de.rki.coronawarnapp.ui.viewmodel.TracingViewModel
import de.rki.coronawarnapp.util.CachedKeyFileHolder
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.*
mertsafter marked this conversation as resolved.
Show resolved Hide resolved
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
Expand All @@ -83,7 +61,9 @@ import org.joda.time.DateTimeZone
import timber.log.Timber
import java.io.File
import java.lang.reflect.Type
import java.util.Date
import java.util.UUID
import kotlin.system.measureTimeMillis

@SuppressWarnings("TooManyFunctions", "MagicNumber", "LongMethod")
class TestForAPIFragment : Fragment(), InternalExposureNotificationPermissionHelper.Callback {
Expand Down Expand Up @@ -120,6 +100,8 @@ class TestForAPIFragment : Fragment(), InternalExposureNotificationPermissionHel
private var _binding: FragmentTestForAPIBinding? = null
private val binding: FragmentTestForAPIBinding get() = _binding!!

private var lastSetCountries: List<String>? = null

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
Expand Down Expand Up @@ -166,6 +148,21 @@ class TestForAPIFragment : Fragment(), InternalExposureNotificationPermissionHel
qrPagerAdapter = QRPagerAdapter()
qrPager.adapter = qrPagerAdapter

// Load countries from App config and update Country UI element states
lifecycleScope.launch {
lastSetCountries =
ApplicationConfigurationService.asyncRetrieveApplicationConfiguration()
.supportedCountriesList

(input_country_codes_editText as EditText).setText(
lastSetCountries?.joinToString(
","
)
)

updateCountryStatusLabel()
}

button_api_test_start.setOnClickListener {
start()
}
Expand Down Expand Up @@ -275,6 +272,31 @@ class TestForAPIFragment : Fragment(), InternalExposureNotificationPermissionHel
showToast(TimeVariables.getActiveTracingDaysInRetentionPeriod().toString())
}
}

button_filter_country_codes.setOnClickListener {
// Get user input country codes
val rawCountryCodes = (input_country_codes_editText as EditText).text.toString()

// Country codes can be separated by space or ,
var countryCodes = rawCountryCodes.split(',', ' ').filter { it.isNotEmpty() }

lastSetCountries = countryCodes

// Trigger asyncFetchFiles which will use all Countries passed as parameter
val currentDate = Date(System.currentTimeMillis())
lifecycleScope.launch {
CachedKeyFileHolder.asyncFetchFiles(currentDate, countryCodes)
updateCountryStatusLabel()
}
}

button_retrieve_diagnosis_keys_and_calc_risk_level.setOnClickListener {
lifecycleScope.launch {
val repeatCount =
(input_measure_risk_key_repeat_count as EditText).text.toString().toInt()
measureRiskLevelAndKeyRetrieval(repeatCount)
}
}
}

override fun onResume() {
Expand All @@ -283,6 +305,123 @@ class TestForAPIFragment : Fragment(), InternalExposureNotificationPermissionHel
updateExposureSummaryDisplay(null)
}

/**
* Calls the RetrieveDiagnosisKeysTransaction and RiskLevelTransaction and measures them.
* Results are displayed using a label
* @param callCount defines how often the transactions should be called (each call will be
* measured separately)
*/
private suspend fun measureRiskLevelAndKeyRetrieval(callCount: Int) {
mertsafter marked this conversation as resolved.
Show resolved Hide resolved
val countries = lastSetCountries

var resultInfo = StringBuilder()
.append(
"MEASUREMENT Running for Countries:\n " +
"${countries?.joinToString(", ")}\n\n"
)
.append("Result: \n\n")
.append("#\t Combined \t Download \t Key Calc \t File # \t Files size\n")

label_test_api_measure_calc_key_status.text = resultInfo.toString()

repeat(callCount) { index ->
var keyRetrievalError = ""
var keyFileCount: Int = -1
var keyFileDownloadDuration: Long = -1
var keyFilesSize: Long = -1

try {
measureDiagnosticKeyRetrieval("#$index") { duration, keyCount, totalFileSize ->
keyFileCount = keyCount
keyFileDownloadDuration = duration
keyFilesSize = totalFileSize
}
} catch (e: TransactionException) {
keyRetrievalError = e.message.toString()
}

var calculationDuration: Long = -1
var calculationError = ""

try {
measureKeyCalculation("#$index") {
calculationDuration = it
}
} catch (e: TransactionException) {
calculationError = e.message.toString()
}

// build result entry for current iteration with all gathered data
resultInfo.append(
"${index + 1}. \t ${calculationDuration + keyFileDownloadDuration} ms \t\t " +
"$keyFileDownloadDuration ms " +
"\t\t $calculationDuration ms \t\t $keyFileCount \t\t $keyFilesSize MB\n"
)

if (keyRetrievalError.isNotEmpty()) {
resultInfo.append("Key Retrieval Error: $keyRetrievalError\n")
}

if (calculationError.isNotEmpty()) {
resultInfo.append("Calculation Error: $calculationError\n")
}

label_test_api_measure_calc_key_status.text = resultInfo.toString()
}
}

private suspend fun measureKeyCalculation(label: String, finished: (duration: Long) -> Unit) {
try {
Timber.v("MEASURE [Risk Level Calculation] $label started")
// start risk level calculation and get duration
measureTimeMillis {
RiskLevelTransaction.start()
}.also {
Timber.v("MEASURE [Risk Level Calculation] $label finished")
finished(it)
}
} catch (e: TransactionException) {
e.report(ExceptionCategory.INTERNAL)
throw e
}
}

private suspend fun measureDiagnosticKeyRetrieval(
label: String,
finished: (duration: Long, keyCount: Int, fileSize: Long) -> Unit
) {
var keyFileDownloadStart: Long = -1

try {
RetrieveDiagnosisKeysTransaction.onKeyFilesStarted = {
Timber.v("MEASURE [Diagnostic Key Files] $label started")
keyFileDownloadStart = System.currentTimeMillis()
}

RetrieveDiagnosisKeysTransaction.onKeyFilesFinished = { count, size ->
Timber.v("MEASURE [Diagnostic Key Files] $label finished")
val duration = System.currentTimeMillis() - keyFileDownloadStart
finished(duration, count, size)
}
// start diagnostic key transaction
RetrieveDiagnosisKeysTransaction.start(lastSetCountries)
} catch (e: TransactionException) {
e.report(ExceptionCategory.INTERNAL)
throw e
}
}

/**
* Updates the Label for country filter
*/
private fun updateCountryStatusLabel() {
label_country_code_filter_status.text =
getString(
R.string.test_api_country_filter_status,
lastSetCountries?.joinToString(", ")
)
}

private val prettyKey = { key: AppleLegacyKeyExchange.Key ->
StringBuilder()
.append("\nKey data: ${key.keyData}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@
<TextView
android:id="@+id/label_exposure_summary"
style="@style/headline6"
android:accessibilityHeading="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:accessibilityHeading="true"
android:text="@string/test_api_exposure_summary_headline" />

<TextView
Expand Down Expand Up @@ -106,10 +106,10 @@
<TextView
android:id="@+id/label_my_keys"
style="@style/headline6"
android:accessibilityHeading="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_normal"
android:accessibilityHeading="true"
android:text="@string/test_api_body_my_keys" />

<TextView
Expand All @@ -134,9 +134,9 @@
<TextView
android:id="@+id/label_other_keys"
style="@style/headline6"
android:accessibilityHeading="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:accessibilityHeading="true"
android:text="@string/test_api_body_other_keys" />

<TextView
Expand Down Expand Up @@ -225,24 +225,101 @@
android:text="Get Active Tracing Duration in Retention Period" />

<TextView
style="@style/headline4"
android:id="@+id/label_country_filter"
style="@style/headline6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_normal"
android:accessibilityHeading="true"
android:text="@string/test_api_country_settings_heading" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">

<EditText
android:id="@+id/input_country_codes_editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.9" />

<Button
android:id="@+id/button_filter_country_codes"
style="@style/buttonPrimary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.1"
android:text="Apply" />
</LinearLayout>

<TextView
android:id="@+id/label_country_code_filter_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/test_api_country_filter_status">

</TextView>

<TextView
android:id="@+id/label_test_api_measure"
style="@style/headline6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_normal"
android:accessibilityHeading="true"
android:text="Statistics" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">

<Button
android:id="@+id/button_retrieve_diagnosis_keys_and_calc_risk_level"
style="@style/buttonPrimary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_normal"
android:layout_marginBottom="@dimen/spacing_normal"
android:layout_weight="1"
android:text="Measure: Calculate Risk Level / Key Retrieval" />

<EditText
android:id="@+id/input_measure_risk_key_repeat_count"
android:layout_width="98dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="number"
android:text="1" />

</LinearLayout>

<TextView
android:id="@+id/label_test_api_measure_calc_key_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Result: "/>

<TextView
style="@style/headline4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:accessibilityHeading="true"
android:text="headline4" />

<TextView
style="@style/headline5"
android:accessibilityHeading="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:accessibilityHeading="true"
android:text="headline5" />

<TextView
style="@style/headline6"
android:accessibilityHeading="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:accessibilityHeading="true"
android:text="headline6" />

<TextView
Expand Down
Loading