Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -99,6 +99,7 @@ class ConversationStateMirrorImplTest {
fun anUpdate_savesTheDatabaseStateWithTheTimeAndVersion() {
databaseStore.blockedDestinations = setOf(BLOCKED_DESTINATION)
databaseStore.archivedThreadIds = setOf(ARCHIVED_THREAD_ID)
databaseStore.unarchivedBlockedThreadIds = setOf(UNARCHIVED_BLOCKED_THREAD_ID)

val updateStartedAt = System.currentTimeMillis()
mirror.update()
Expand All @@ -107,6 +108,10 @@ class ConversationStateMirrorImplTest {
val mirroredState = preferencesStore.mirroredState
assertEquals(setOf(BLOCKED_DESTINATION), mirroredState.blockedDestinations)
assertEquals(setOf(ARCHIVED_THREAD_ID), mirroredState.archivedThreadIds)
assertEquals(
setOf(UNARCHIVED_BLOCKED_THREAD_ID),
mirroredState.unarchivedBlockedThreadIds,
)
assertEquals(DATABASE_VERSION, mirroredState.databaseVersion)
assertTrue(mirroredState.mirroredAt in updateStartedAt..updateEndedAt)
}
Expand Down Expand Up @@ -259,6 +264,7 @@ class ConversationStateMirrorImplTest {
var mirroredState = MirroredConversationState(
blockedDestinations = emptySet(),
archivedThreadIds = emptySet(),
unarchivedBlockedThreadIds = emptySet(),
mirroredAt = 0L,
databaseVersion = 0,
)
Expand Down Expand Up @@ -295,6 +301,9 @@ class ConversationStateMirrorImplTest {
@Volatile
var archivedThreadIds = emptySet<String>()

@Volatile
var unarchivedBlockedThreadIds = emptySet<String>()

@Volatile
var onReadArchivedThreadIds: () -> Unit = {}

Expand All @@ -311,6 +320,10 @@ class ConversationStateMirrorImplTest {
return archivedThreadIds
}

override fun readUnarchivedBlockedThreadIds(): Set<String> {
return unarchivedBlockedThreadIds
}

override fun restore(mirroredState: MirroredConversationState) {
restoreFailure?.let { failure -> throw failure }
restoredStates += mirroredState
Expand All @@ -322,6 +335,7 @@ class ConversationStateMirrorImplTest {
private const val BLOCKED_DESTINATION = "+15550100"
private const val NEWLY_BLOCKED_DESTINATION = "+15550101"
private const val ARCHIVED_THREAD_ID = "12"
private const val UNARCHIVED_BLOCKED_THREAD_ID = "13"
private const val DATABASE_VERSION = 5
private const val FULL_SYNC_TIME = 2_000L
private const val TIMEOUT_MILLIS = 10_000L
Expand All @@ -330,6 +344,7 @@ class ConversationStateMirrorImplTest {
private val MIRRORED_STATE = MirroredConversationState(
blockedDestinations = setOf(BLOCKED_DESTINATION),
archivedThreadIds = setOf(ARCHIVED_THREAD_ID),
unarchivedBlockedThreadIds = setOf(UNARCHIVED_BLOCKED_THREAD_ID),
mirroredAt = 1_000L,
databaseVersion = DATABASE_VERSION,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.android.messaging.data.conversationstate.store

import android.content.Context
import android.database.sqlite.SQLiteFullException
import androidx.core.content.contentValuesOf
import com.android.messaging.FactoryTestAccess
import com.android.messaging.data.conversationstate.model.MirroredConversationState
Expand All @@ -12,13 +13,15 @@ import com.android.messaging.datamodel.DatabaseHelper.ParticipantColumns
import com.android.messaging.datamodel.DatabaseWrapper
import com.android.messaging.datamodel.createInMemoryActionSyncTestDatabase
import com.android.messaging.datamodel.data.ParticipantData
import com.android.messaging.datamodel.simulateFullDisk
import com.android.messaging.testutil.installTestFactory
import io.mockk.every
import io.mockk.mockk
import io.mockk.unmockkAll
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertThrows
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
Expand Down Expand Up @@ -95,6 +98,29 @@ class ConversationStateDatabaseStoreImplTest {
assertEquals(setOf(ARCHIVED_THREAD_ID.toString()), store.readArchivedThreadIds())
}

@Test
fun readUnarchivedBlockedThreadIds_readsTheBlockedSendersSyncedThreadsInTheInbox() {
db.insertSyncedConversation(
destination = BLOCKED_DESTINATION,
threadId = BLOCKED_THREAD_ID,
isBlocked = true,
)
db.insertSyncedConversation(
destination = ARCHIVED_DESTINATION,
threadId = ARCHIVED_THREAD_ID,
isBlocked = true,
isArchived = true,
)
db.insertSyncedConversation(destination = OTHER_DESTINATION, threadId = OTHER_THREAD_ID)
db.insertSyncedConversation(
destination = UNARCHIVED_BLOCKED_DESTINATION,
threadId = NO_THREAD_ID,
isBlocked = true,
)

assertEquals(setOf(BLOCKED_THREAD_ID.toString()), store.readUnarchivedBlockedThreadIds())
}

@Test
fun restore_blocksAndArchivesTheResyncedConversations() {
insertResyncedConversations(sortTimestamp = SYNCED_SORT_TIMESTAMP)
Expand Down Expand Up @@ -125,6 +151,35 @@ class ConversationStateDatabaseStoreImplTest {
assertFalse(db.isArchived(threadId = ARCHIVED_THREAD_ID))
}

@Test
fun restore_leavesTheBlockedSendersConversationsThatWereInTheInboxThere() {
insertResyncedConversations(sortTimestamp = SYNCED_SORT_TIMESTAMP)

store.restore(mirroredState = MIRRORED_STATE)

assertTrue(db.isBlocked(destination = UNARCHIVED_BLOCKED_DESTINATION))
assertFalse(db.isArchived(threadId = UNARCHIVED_BLOCKED_THREAD_ID))
}

@Test
fun restore_onAFullDisk_failsAndRestoresNothing() {
insertResyncedConversations(sortTimestamp = SYNCED_SORT_TIMESTAMP)
db.database.simulateFullDisk()

assertThrows(SQLiteFullException::class.java) {
store.restore(
mirroredState = MIRRORED_STATE.copy(
blockedDestinations = MIRRORED_STATE.blockedDestinations +
OVERSIZED_DESTINATION,
),
)
}

assertFalse(db.isBlocked(destination = BLOCKED_DESTINATION))
assertFalse(db.isArchived(threadId = BLOCKED_THREAD_ID))
assertFalse(db.isArchived(threadId = ARCHIVED_THREAD_ID))
}

private fun insertResyncedConversations(sortTimestamp: Long) {
db.insertSyncedConversation(
destination = BLOCKED_DESTINATION,
Expand All @@ -141,6 +196,11 @@ class ConversationStateDatabaseStoreImplTest {
threadId = OTHER_THREAD_ID,
sortTimestamp = sortTimestamp,
)
db.insertSyncedConversation(
destination = UNARCHIVED_BLOCKED_DESTINATION,
threadId = UNARCHIVED_BLOCKED_THREAD_ID,
sortTimestamp = sortTimestamp,
)
}

private fun DatabaseWrapper.insertSyncedConversation(
Expand Down Expand Up @@ -211,19 +271,24 @@ class ConversationStateDatabaseStoreImplTest {
private const val BLOCKED_DESTINATION = "+15550100"
private const val ARCHIVED_DESTINATION = "+15550101"
private const val OTHER_DESTINATION = "+15550102"
private const val UNARCHIVED_BLOCKED_DESTINATION = "+15550103"
private const val SELF_DESTINATION = "+15550199"
private const val SELF_SUB_ID = 1
private const val BLOCKED_THREAD_ID = 11L
private const val ARCHIVED_THREAD_ID = 12L
private const val OTHER_THREAD_ID = 13L
private const val UNARCHIVED_BLOCKED_THREAD_ID = 14L
private const val NO_THREAD_ID = 0L
private const val MIRRORED_AT_MILLIS = 2_000L

private const val SYNCED_SORT_TIMESTAMP = 1_000L

private val OVERSIZED_DESTINATION = "+1${"5".repeat(50_000)}"

private val MIRRORED_STATE = MirroredConversationState(
blockedDestinations = setOf(BLOCKED_DESTINATION),
blockedDestinations = setOf(BLOCKED_DESTINATION, UNARCHIVED_BLOCKED_DESTINATION),
archivedThreadIds = setOf(BLOCKED_THREAD_ID.toString(), ARCHIVED_THREAD_ID.toString()),
unarchivedBlockedThreadIds = setOf(UNARCHIVED_BLOCKED_THREAD_ID.toString()),
mirroredAt = MIRRORED_AT_MILLIS,
databaseVersion = 1,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.android.messaging.data.conversationstate.store

import android.content.Context
import androidx.core.content.edit
import com.android.messaging.data.conversationstate.model.MirroredConversationState
import com.android.messaging.testutil.backupRulesExcluding
import org.junit.Assert.assertEquals
Expand Down Expand Up @@ -37,6 +38,20 @@ class ConversationStatePreferencesStoreImplTest {
)
}

@Test
fun aMirrorSavedBeforeUnarchivedBlockedThreadsWereKept_readsNone() {
context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE).edit(commit = true) {
putStringSet("blocked_destinations", MIRRORED_STATE.blockedDestinations)
putStringSet("archived_thread_ids", MIRRORED_STATE.archivedThreadIds)
}

val mirroredState = ConversationStatePreferencesStoreImpl(context = context)
.readMirroredState()

assertEquals(MIRRORED_STATE.blockedDestinations, mirroredState.blockedDestinations)
assertEquals(emptySet<String>(), mirroredState.unarchivedBlockedThreadIds)
}

@Test
fun aPendingRestore_staysPendingUntilCleared() {
ConversationStatePreferencesStoreImpl(context = context).markRestorePending()
Expand All @@ -60,6 +75,7 @@ class ConversationStatePreferencesStoreImplTest {
setOf(
"blocked_destinations",
"archived_thread_ids",
"unarchived_blocked_thread_ids",
"mirrored_at",
"database_version",
"restore_pending",
Expand All @@ -78,6 +94,7 @@ class ConversationStatePreferencesStoreImplTest {
private val MIRRORED_STATE = MirroredConversationState(
blockedDestinations = setOf("+15550100", "+15550101"),
archivedThreadIds = setOf("12"),
unarchivedBlockedThreadIds = setOf("13"),
mirroredAt = 1_700_000_000_000L,
databaseVersion = 5,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.android.messaging.data.participantdestination.store.ParticipantDestin
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertThrows
import org.junit.Assert.assertTrue
import org.junit.Test

Expand Down Expand Up @@ -252,6 +253,18 @@ class ParticipantDestinationNormalizerImplTest {
assertEquals(setOf(US_SUB_ID), preferencesStore.savedNormalization?.subIds)
}

@Test
fun aFailedRenormalization_staysPending() {
databaseStore.lastParticipantId = 7L
bareNumberReader.readings = listOf(BARE_READING)
databaseStore.applyFailure = IllegalStateException(DATABASE_FULL)

assertThrows(IllegalStateException::class.java) { normalizer.renormalizeIfPending() }

assertNull(preferencesStore.savedNormalization)
assertEquals(emptySet<Int>(), preferencesStore.normalizedSubIds)
}

@Test
fun aRun_readsAndSavesHoldingTheLock() {
databaseStore.lastParticipantId = 7L
Expand Down Expand Up @@ -413,6 +426,7 @@ class ParticipantDestinationNormalizerImplTest {
var senderDestinations = emptySet<String>()
var senderDestinationReadCount = 0
var appliedRenormalizations: List<Renormalization>? = null
var applyFailure: RuntimeException? = null

override fun readBareNumberParticipants(): List<BareNumberParticipant> {
error("The normalizer reads the participants through the reader")
Expand All @@ -435,6 +449,7 @@ class ParticipantDestinationNormalizerImplTest {

override fun applyRenormalizations(renormalizations: List<Renormalization>) {
recordCall(name = "applyRenormalizations")
applyFailure?.let { failure -> throw failure }
appliedRenormalizations = renormalizations
}
}
Expand Down Expand Up @@ -477,6 +492,7 @@ class ParticipantDestinationNormalizerImplTest {

private companion object {
private const val DATABASE_VERSION = 5
private const val DATABASE_FULL = "database or disk is full"
private const val US_SUB_ID = 1
private const val FO_SUB_ID = 2
private const val BARE_DESTINATION = "211234"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.android.messaging.data.participantdestination.store

import android.content.Context
import android.database.sqlite.SQLiteFullException
import androidx.core.content.contentValuesOf
import com.android.messaging.FactoryTestAccess
import com.android.messaging.data.conversation.model.ParticipantId
Expand All @@ -15,15 +16,18 @@ import com.android.messaging.datamodel.DatabaseHelper.ParticipantColumns
import com.android.messaging.datamodel.DatabaseWrapper
import com.android.messaging.datamodel.createInMemoryActionSyncTestDatabase
import com.android.messaging.datamodel.data.ParticipantData
import com.android.messaging.datamodel.simulateFullDisk
import com.android.messaging.testutil.installTestFactory
import io.mockk.every
import io.mockk.unmockkAll
import java.util.concurrent.Callable
import java.util.concurrent.ExecutionException
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertThrows
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
Expand Down Expand Up @@ -147,6 +151,44 @@ class ParticipantDestinationDatabaseStoreImplTest {
)
}

@Test
fun aFullDisk_failsTheRenormalizationsAndAppliesNone() {
val bareId = db.insertParticipant(destination = BARE_NUMBER)
val twinId = db.insertParticipant(destination = CANONICAL_NUMBER)
val loneBareId = db.insertParticipant(destination = LONE_BARE_NUMBER)
val directId = db.insertConversation(participantIds = listOf(bareId))
val participants = databaseStore.readBareNumberParticipants()
.associateBy { participant -> participant.destination }
db.database.simulateFullDisk()

val failure = assertThrows(ExecutionException::class.java) {
applyRenormalizations(
renormalizations = listOf(
Renormalization(
participant = participants.getValue(LONE_BARE_NUMBER),
canonicalDestination = OVERSIZED_DESTINATION,
),
Renormalization(
participant = participants.getValue(BARE_NUMBER),
canonicalDestination = CANONICAL_NUMBER,
),
),
)
}

assertTrue("not a full disk: ${failure.cause}", failure.cause is SQLiteFullException)
assertTrue("the bare row was merged", db.participantExists(id = bareId))
assertEquals(listOf(bareId), db.participantsOf(conversationId = directId))
assertTrue(db.participantExists(id = twinId))
assertEquals(
LONE_BARE_NUMBER,
db.participantColumn(
id = loneBareId,
column = ParticipantColumns.NORMALIZED_DESTINATION,
),
)
}

@Test
fun rowsThatAreNotBareNumbers_areNotReadAsBareNumbers() {
val bareId = db.insertParticipant(destination = BARE_NUMBER, isBlocked = true)
Expand Down Expand Up @@ -242,6 +284,10 @@ class ParticipantDestinationDatabaseStoreImplTest {
canonicalDestination = CANONICAL_DESTINATIONS.getValue(participant.destination),
)
}
applyRenormalizations(renormalizations = renormalizations)
}

private fun applyRenormalizations(renormalizations: List<Renormalization>) {
val executor = Executors.newSingleThreadExecutor()
try {
executor.submit(
Expand Down Expand Up @@ -425,6 +471,8 @@ class ParticipantDestinationDatabaseStoreImplTest {
const val CONTACT_NAME = "Repro Estonia"
const val TIMEOUT_SECONDS = 10L

val OVERSIZED_DESTINATION = "+372${"5".repeat(50_000)}"

val CANONICAL_DESTINATIONS = mapOf(
BARE_NUMBER to CANONICAL_NUMBER,
LONE_BARE_NUMBER to LONE_CANONICAL_NUMBER,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,13 @@ internal fun createInMemoryActionSyncTestDatabase(context: Context): DatabaseWra

return DatabaseWrapper(context, sqliteDatabase)
}

internal fun SQLiteDatabase.simulateFullDisk() {
val pageCount = rawQuery("PRAGMA page_count", null).use { cursor ->
cursor.moveToFirst()
cursor.getLong(0)
}
rawQuery("PRAGMA max_page_count = $pageCount", null).use { cursor ->
cursor.moveToFirst()
}
}
Loading
Loading