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

Overhaul of DiagnosisKey related Download and Caching Mechanisms (EXPOSUREAPP-2469) #1136

Merged
merged 29 commits into from
Sep 11, 2020
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b754d07
Rework of keyfile download and caching.
d4rken Sep 8, 2020
c67eaf9
First batch of unit tests and some fixes for incorrect behavior that …
d4rken Sep 8, 2020
3000bdc
Added unit tests for the KeyCacheRepository
d4rken Sep 8, 2020
f256fef
Implemented POC for migration old key files.
d4rken Sep 8, 2020
5671e66
Fixed legacy file migration and cleanup, improved logging.
d4rken Sep 8, 2020
0a1ffb3
Added unit tests for legacy key file migration.
d4rken Sep 9, 2020
8905532
Add fallback for different file hashes in the header.
d4rken Sep 9, 2020
15ec9e5
Yes kLint, we know it's a long method,
d4rken Sep 9, 2020
4269019
More linting issues, adjusting project code style prevent a few of th…
d4rken Sep 9, 2020
e8f43fb
Added missing unit tests for `KeyFileDownloader` and fixed faulty beh…
d4rken Sep 9, 2020
709c8fb
CRUD (instrumentation) test for `KeyCacheDatabase`
d4rken Sep 9, 2020
2ba7dc6
Remove unused `FileStorageHelper` and related constants+tests.
d4rken Sep 9, 2020
fc8fe52
Fix last3Hours unit test in deviceRelease mode, we need to explicitly…
d4rken Sep 9, 2020
11bf270
Until we have more information about the hashsum's format in the head…
d4rken Sep 10, 2020
76bc35b
Split app config server API from diagnosis key download API,
d4rken Sep 10, 2020
6e96a60
Add test to check that the cache is used on flaky connections.
d4rken Sep 10, 2020
3143c09
Code changes based on PR comment, part #1.
d4rken Sep 10, 2020
949f8f2
Code fluff, formatting.
d4rken Sep 10, 2020
fa196fc
Handle download errors correctly.
d4rken Sep 10, 2020
bcc6418
Refactoring:
d4rken Sep 10, 2020
7fd919b
Let legacy cache migration abort early, depending on whether the key …
d4rken Sep 10, 2020
4fa414a
If we can't create the base directory for the key repo, throw an exce…
d4rken Sep 10, 2020
610aa6e
Delete cache entry if a download fails.
d4rken Sep 10, 2020
d694bac
Fixed test regression due to refactoring.
d4rken Sep 10, 2020
e16e6b4
Consolidate staleness check into `getStale`
d4rken Sep 11, 2020
8c4e312
Consolidate clean up for failed downloads into the download method.
d4rken Sep 11, 2020
10407cf
Because the hour-mode uses caching too, we add an explicit button to …
d4rken Sep 11, 2020
5b87f7f
Add comment with reference to ticket regarding follow up on the other…
d4rken Sep 11, 2020
5f56f0c
Move expected storage size per country into a named constant.
d4rken Sep 11, 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
2 changes: 2 additions & 0 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Corona-Warn-App/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,8 @@ dependencies {
testImplementation "io.kotest:kotest-runner-junit5:4.2.0"
testImplementation "io.kotest:kotest-assertions-core-jvm:4.2.0"
testImplementation "io.kotest:kotest-property-jvm:4.2.0"
androidTestImplementation "io.kotest:kotest-assertions-core-jvm:4.2.0"
androidTestImplementation "io.kotest:kotest-property-jvm:4.2.0"

// Testing - Instrumentation
androidTestImplementation 'androidx.test:runner:1.2.0'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "c4ef5f7d4d9672d11c8eb97a63d4a3c5",
"entities": [
{
"tableName": "keyfiles",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `type` TEXT NOT NULL, `location` TEXT NOT NULL, `day` TEXT NOT NULL, `hour` TEXT, `createdAt` TEXT NOT NULL, `checksumMD5` TEXT, `completed` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "location",
"columnName": "location",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "day",
"columnName": "day",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "hour",
"columnName": "hour",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "createdAt",
"columnName": "createdAt",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "checksumMD5",
"columnName": "checksumMD5",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "isDownloadComplete",
"columnName": "completed",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c4ef5f7d4d9672d11c8eb97a63d4a3c5')"
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "03f6e8cba631c8ef60e506006913a1ad",
"entities": [
{
"tableName": "keyfiles",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `type` TEXT NOT NULL, `location` TEXT NOT NULL, `day` TEXT NOT NULL, `hour` TEXT, `sourceUrl` TEXT NOT NULL, `createdAt` TEXT NOT NULL, `checksumMD5` TEXT, `completed` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "location",
"columnName": "location",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "day",
"columnName": "day",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "hour",
"columnName": "hour",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "sourceUrl",
"columnName": "sourceUrl",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "createdAt",
"columnName": "createdAt",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "checksumMD5",
"columnName": "checksumMD5",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "isDownloadComplete",
"columnName": "completed",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '03f6e8cba631c8ef60e506006913a1ad')"
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package de.rki.coronawarnapp.diagnosiskeys.storage

import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import de.rki.coronawarnapp.diagnosiskeys.server.LocationCode
import io.kotest.matchers.shouldBe
import kotlinx.coroutines.runBlocking
import org.joda.time.Instant
import org.joda.time.LocalDate
import org.joda.time.LocalTime
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class KeyCacheDatabaseTest {
private val database = KeyCacheDatabase.Factory(
ApplicationProvider.getApplicationContext<Context>()
).create()
private val dao = database.cachedKeyFiles()

@Test
fun crud() {
val keyDay = CachedKeyInfo(
type = CachedKeyInfo.Type.COUNTRY_DAY,
location = LocationCode("DE"),
day = LocalDate.now(),
hour = null,
createdAt = Instant.now()
)
val keyHour = CachedKeyInfo(
type = CachedKeyInfo.Type.COUNTRY_HOUR,
location = LocationCode("DE"),
day = LocalDate.now(),
hour = LocalTime.now(),
createdAt = Instant.now()
)
runBlocking {
dao.clear()

dao.insertEntry(keyDay)
dao.insertEntry(keyHour)
dao.getAllEntries() shouldBe listOf(keyDay, keyHour)
dao.getEntriesForType(CachedKeyInfo.Type.COUNTRY_DAY.typeValue) shouldBe listOf(keyDay)
dao.getEntriesForType(CachedKeyInfo.Type.COUNTRY_HOUR.typeValue) shouldBe listOf(keyHour)

dao.updateDownloadState(keyDay.toDownloadUpdate("coffee"))
dao.getEntriesForType(CachedKeyInfo.Type.COUNTRY_DAY.typeValue).single().apply {
isDownloadComplete shouldBe true
checksumMD5 shouldBe "coffee"
}
dao.getEntriesForType(CachedKeyInfo.Type.COUNTRY_HOUR.typeValue).single().apply {
isDownloadComplete shouldBe false
checksumMD5 shouldBe null
}

dao.updateDownloadState(keyHour.toDownloadUpdate("with milk"))
dao.getEntriesForType(CachedKeyInfo.Type.COUNTRY_DAY.typeValue).single().apply {
isDownloadComplete shouldBe true
checksumMD5 shouldBe "coffee"
}
dao.getEntriesForType(CachedKeyInfo.Type.COUNTRY_HOUR.typeValue).single().apply {
isDownloadComplete shouldBe true
checksumMD5 shouldBe "with milk"
}

dao.deleteEntry(keyDay)
dao.getAllEntries() shouldBe listOf(
keyHour.copy(
isDownloadComplete = true,
checksumMD5 = "with milk"
)
)

dao.clear()
dao.getAllEntries() shouldBe emptyList()
}
}
}

This file was deleted.

Loading