mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-24 19:56:00 +00:00
Migrate to shared class hierarchy for unit based database tests.
This commit is contained in:
committed by
Greyson Parrelli
parent
d5e18a8bd5
commit
d0b6d6fdeb
@@ -1,114 +1,96 @@
|
||||
package org.thoughtcrime.securesms.database
|
||||
|
||||
import android.app.Application
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.thoughtcrime.securesms.database.model.StoryType
|
||||
import org.thoughtcrime.securesms.database.model.StoryViewState
|
||||
import org.thoughtcrime.securesms.testing.TestDatabaseUtil
|
||||
import org.thoughtcrime.securesms.testutil.SignalDatabaseRule
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(manifest = Config.NONE, application = Application::class)
|
||||
class MmsDatabaseTest {
|
||||
private lateinit var db: SQLiteDatabase
|
||||
private lateinit var messageTable: MessageTable
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
val sqlCipher = TestDatabaseUtil.inMemoryDatabase {
|
||||
execSQL(MessageTable.CREATE_TABLE)
|
||||
}
|
||||
|
||||
db = sqlCipher.myWritableDatabase
|
||||
messageTable = MessageTable(ApplicationProvider.getApplicationContext(), sqlCipher)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
db.close()
|
||||
}
|
||||
@get:Rule
|
||||
val signalDatabaseRule = SignalDatabaseRule()
|
||||
|
||||
@Test
|
||||
fun `isGroupQuitMessage when normal message, return false`() {
|
||||
val id = TestMms.insert(db, type = MessageTypes.BASE_SENDING_TYPE or MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.PUSH_MESSAGE_BIT)
|
||||
assertFalse(messageTable.isGroupQuitMessage(id))
|
||||
val id = TestMms.insert(signalDatabaseRule.writeableDatabase, type = MessageTypes.BASE_SENDING_TYPE or MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.PUSH_MESSAGE_BIT)
|
||||
assertFalse(SignalDatabase.messages.isGroupQuitMessage(id))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isGroupQuitMessage when legacy quit message, return true`() {
|
||||
val id = TestMms.insert(db, type = MessageTypes.BASE_SENDING_TYPE or MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.PUSH_MESSAGE_BIT or MessageTypes.GROUP_LEAVE_BIT)
|
||||
assertTrue(messageTable.isGroupQuitMessage(id))
|
||||
val id = TestMms.insert(signalDatabaseRule.writeableDatabase, type = MessageTypes.BASE_SENDING_TYPE or MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.PUSH_MESSAGE_BIT or MessageTypes.GROUP_LEAVE_BIT)
|
||||
assertTrue(SignalDatabase.messages.isGroupQuitMessage(id))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isGroupQuitMessage when GV2 leave update, return false`() {
|
||||
val id = TestMms.insert(db, type = MessageTypes.BASE_SENDING_TYPE or MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.PUSH_MESSAGE_BIT or MessageTypes.GROUP_LEAVE_BIT or MessageTypes.GROUP_V2_BIT or MessageTypes.GROUP_UPDATE_BIT)
|
||||
assertFalse(messageTable.isGroupQuitMessage(id))
|
||||
val id = TestMms.insert(signalDatabaseRule.writeableDatabase, type = MessageTypes.BASE_SENDING_TYPE or MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.PUSH_MESSAGE_BIT or MessageTypes.GROUP_LEAVE_BIT or MessageTypes.GROUP_V2_BIT or MessageTypes.GROUP_UPDATE_BIT)
|
||||
assertFalse(SignalDatabase.messages.isGroupQuitMessage(id))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getLatestGroupQuitTimestamp when only normal message, return -1`() {
|
||||
TestMms.insert(db, threadId = 1, sentTimeMillis = 1, type = MessageTypes.BASE_SENDING_TYPE or MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.PUSH_MESSAGE_BIT)
|
||||
assertEquals(-1, messageTable.getLatestGroupQuitTimestamp(1, 4))
|
||||
TestMms.insert(signalDatabaseRule.writeableDatabase, threadId = 1, sentTimeMillis = 1, type = MessageTypes.BASE_SENDING_TYPE or MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.PUSH_MESSAGE_BIT)
|
||||
assertEquals(-1, SignalDatabase.messages.getLatestGroupQuitTimestamp(1, 4))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getLatestGroupQuitTimestamp when legacy quit, return message timestamp`() {
|
||||
TestMms.insert(db, threadId = 1, sentTimeMillis = 2, type = MessageTypes.BASE_SENDING_TYPE or MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.PUSH_MESSAGE_BIT or MessageTypes.GROUP_LEAVE_BIT)
|
||||
assertEquals(2, messageTable.getLatestGroupQuitTimestamp(1, 4))
|
||||
TestMms.insert(signalDatabaseRule.writeableDatabase, threadId = 1, sentTimeMillis = 2, type = MessageTypes.BASE_SENDING_TYPE or MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.PUSH_MESSAGE_BIT or MessageTypes.GROUP_LEAVE_BIT)
|
||||
assertEquals(2, SignalDatabase.messages.getLatestGroupQuitTimestamp(1, 4))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getLatestGroupQuitTimestamp when GV2 leave update message, return -1`() {
|
||||
TestMms.insert(db, threadId = 1, sentTimeMillis = 3, type = MessageTypes.BASE_SENDING_TYPE or MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.PUSH_MESSAGE_BIT or MessageTypes.GROUP_LEAVE_BIT or MessageTypes.GROUP_V2_BIT or MessageTypes.GROUP_UPDATE_BIT)
|
||||
assertEquals(-1, messageTable.getLatestGroupQuitTimestamp(1, 4))
|
||||
TestMms.insert(signalDatabaseRule.writeableDatabase, threadId = 1, sentTimeMillis = 3, type = MessageTypes.BASE_SENDING_TYPE or MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.PUSH_MESSAGE_BIT or MessageTypes.GROUP_LEAVE_BIT or MessageTypes.GROUP_V2_BIT or MessageTypes.GROUP_UPDATE_BIT)
|
||||
assertEquals(-1, SignalDatabase.messages.getLatestGroupQuitTimestamp(1, 4))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given no stories in database, when I getStoryViewState, then I expect NONE`() {
|
||||
assertEquals(StoryViewState.NONE, messageTable.getStoryViewState(1))
|
||||
assertEquals(StoryViewState.NONE, SignalDatabase.messages.getStoryViewState(1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given stories in database not in thread 1, when I getStoryViewState for thread 1, then I expect NONE`() {
|
||||
TestMms.insert(db, threadId = 2, storyType = StoryType.STORY_WITH_REPLIES)
|
||||
TestMms.insert(db, threadId = 2, storyType = StoryType.STORY_WITH_REPLIES)
|
||||
assertEquals(StoryViewState.NONE, messageTable.getStoryViewState(1))
|
||||
TestMms.insert(signalDatabaseRule.writeableDatabase, threadId = 2, storyType = StoryType.STORY_WITH_REPLIES)
|
||||
TestMms.insert(signalDatabaseRule.writeableDatabase, threadId = 2, storyType = StoryType.STORY_WITH_REPLIES)
|
||||
assertEquals(StoryViewState.NONE, SignalDatabase.messages.getStoryViewState(1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given viewed incoming stories in database, when I getStoryViewState, then I expect VIEWED`() {
|
||||
TestMms.insert(db, threadId = 1, storyType = StoryType.STORY_WITH_REPLIES, viewed = true)
|
||||
TestMms.insert(db, threadId = 1, storyType = StoryType.STORY_WITH_REPLIES, viewed = true)
|
||||
assertEquals(StoryViewState.VIEWED, messageTable.getStoryViewState(1))
|
||||
TestMms.insert(signalDatabaseRule.writeableDatabase, threadId = 1, storyType = StoryType.STORY_WITH_REPLIES, viewed = true)
|
||||
TestMms.insert(signalDatabaseRule.writeableDatabase, threadId = 1, storyType = StoryType.STORY_WITH_REPLIES, viewed = true)
|
||||
assertEquals(StoryViewState.VIEWED, SignalDatabase.messages.getStoryViewState(1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given unviewed incoming stories in database, when I getStoryViewState, then I expect UNVIEWED`() {
|
||||
TestMms.insert(db, threadId = 1, storyType = StoryType.STORY_WITH_REPLIES, viewed = false)
|
||||
TestMms.insert(db, threadId = 1, storyType = StoryType.STORY_WITH_REPLIES, viewed = false)
|
||||
assertEquals(StoryViewState.UNVIEWED, messageTable.getStoryViewState(1))
|
||||
TestMms.insert(signalDatabaseRule.writeableDatabase, threadId = 1, storyType = StoryType.STORY_WITH_REPLIES, viewed = false)
|
||||
TestMms.insert(signalDatabaseRule.writeableDatabase, threadId = 1, storyType = StoryType.STORY_WITH_REPLIES, viewed = false)
|
||||
assertEquals(StoryViewState.UNVIEWED, SignalDatabase.messages.getStoryViewState(1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given mix of viewed and unviewed incoming stories in database, when I getStoryViewState, then I expect UNVIEWED`() {
|
||||
TestMms.insert(db, threadId = 1, storyType = StoryType.STORY_WITH_REPLIES, viewed = true)
|
||||
TestMms.insert(db, threadId = 1, storyType = StoryType.STORY_WITH_REPLIES, viewed = false)
|
||||
assertEquals(StoryViewState.UNVIEWED, messageTable.getStoryViewState(1))
|
||||
TestMms.insert(signalDatabaseRule.writeableDatabase, threadId = 1, storyType = StoryType.STORY_WITH_REPLIES, viewed = true)
|
||||
TestMms.insert(signalDatabaseRule.writeableDatabase, threadId = 1, storyType = StoryType.STORY_WITH_REPLIES, viewed = false)
|
||||
assertEquals(StoryViewState.UNVIEWED, SignalDatabase.messages.getStoryViewState(1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Given only outgoing story in database, when I getStoryViewState, then I expect VIEWED`() {
|
||||
TestMms.insert(db, threadId = 1, storyType = StoryType.STORY_WITH_REPLIES, type = MessageTypes.BASE_OUTBOX_TYPE)
|
||||
assertEquals(StoryViewState.VIEWED, messageTable.getStoryViewState(1))
|
||||
TestMms.insert(signalDatabaseRule.writeableDatabase, threadId = 1, storyType = StoryType.STORY_WITH_REPLIES, type = MessageTypes.BASE_OUTBOX_TYPE)
|
||||
assertEquals(StoryViewState.VIEWED, SignalDatabase.messages.getStoryViewState(1))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,26 @@
|
||||
package org.thoughtcrime.securesms.database
|
||||
|
||||
import android.app.Application
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.signal.core.util.CursorUtil
|
||||
import org.thoughtcrime.securesms.testing.TestDatabaseUtil
|
||||
import org.thoughtcrime.securesms.testutil.SignalDatabaseRule
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(manifest = Config.NONE, application = Application::class)
|
||||
class MmsSmsDatabaseTest {
|
||||
|
||||
private lateinit var messageTable: MessageTable
|
||||
private lateinit var db: SQLiteDatabase
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
val sqlCipher = TestDatabaseUtil.inMemoryDatabase {
|
||||
execSQL(MessageTable.CREATE_TABLE)
|
||||
MessageTable.CREATE_INDEXS.forEach { execSQL(it) }
|
||||
}
|
||||
|
||||
db = sqlCipher.myWritableDatabase
|
||||
messageTable = MessageTable(ApplicationProvider.getApplicationContext(), sqlCipher)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
db.close()
|
||||
}
|
||||
@get:Rule
|
||||
val signalDatabaseRule = SignalDatabaseRule()
|
||||
|
||||
@Test
|
||||
fun `getConversationSnippet when single normal SMS, return SMS message id and transport as false`() {
|
||||
TestSms.insert(db)
|
||||
messageTable.getConversationSnippetCursor(1).use { cursor ->
|
||||
TestSms.insert(signalDatabaseRule.writeableDatabase)
|
||||
SignalDatabase.messages.getConversationSnippetCursor(1).use { cursor ->
|
||||
cursor.moveToFirst()
|
||||
assertEquals(1, CursorUtil.requireLong(cursor, MessageTable.ID))
|
||||
}
|
||||
@@ -47,8 +28,8 @@ class MmsSmsDatabaseTest {
|
||||
|
||||
@Test
|
||||
fun `getConversationSnippet when single normal MMS, return MMS message id and transport as true`() {
|
||||
TestMms.insert(db)
|
||||
messageTable.getConversationSnippetCursor(1).use { cursor ->
|
||||
TestMms.insert(signalDatabaseRule.writeableDatabase)
|
||||
SignalDatabase.messages.getConversationSnippetCursor(1).use { cursor ->
|
||||
cursor.moveToFirst()
|
||||
assertEquals(1, CursorUtil.requireLong(cursor, MessageTable.ID))
|
||||
}
|
||||
@@ -58,14 +39,14 @@ class MmsSmsDatabaseTest {
|
||||
fun `getConversationSnippet when single normal MMS then GV2 leave update message, return MMS message id and transport as true both times`() {
|
||||
val timestamp = System.currentTimeMillis()
|
||||
|
||||
TestMms.insert(db, receivedTimestampMillis = timestamp + 2)
|
||||
messageTable.getConversationSnippetCursor(1).use { cursor ->
|
||||
TestMms.insert(signalDatabaseRule.writeableDatabase, receivedTimestampMillis = timestamp + 2)
|
||||
SignalDatabase.messages.getConversationSnippetCursor(1).use { cursor ->
|
||||
cursor.moveToFirst()
|
||||
assertEquals(1, CursorUtil.requireLong(cursor, MessageTable.ID))
|
||||
}
|
||||
|
||||
TestSms.insert(db, receivedTimestampMillis = timestamp + 3, type = MessageTypes.BASE_SENDING_TYPE or MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.PUSH_MESSAGE_BIT or MessageTypes.GROUP_V2_LEAVE_BITS)
|
||||
messageTable.getConversationSnippetCursor(1).use { cursor ->
|
||||
TestSms.insert(signalDatabaseRule.writeableDatabase, receivedTimestampMillis = timestamp + 3, type = MessageTypes.BASE_SENDING_TYPE or MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.PUSH_MESSAGE_BIT or MessageTypes.GROUP_V2_LEAVE_BITS)
|
||||
SignalDatabase.messages.getConversationSnippetCursor(1).use { cursor ->
|
||||
cursor.moveToFirst()
|
||||
assertEquals(1, CursorUtil.requireLong(cursor, MessageTable.ID))
|
||||
}
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
package org.thoughtcrime.securesms.database
|
||||
|
||||
import android.app.Application
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.containsExactlyInAnyOrder
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isFalse
|
||||
import assertk.assertions.isTrue
|
||||
import assertk.assertions.single
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
@@ -19,8 +15,8 @@ import org.robolectric.annotation.Config
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColor
|
||||
import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.testing.TestDatabaseUtil
|
||||
import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule
|
||||
import org.thoughtcrime.securesms.testutil.SignalDatabaseRule
|
||||
import java.time.DayOfWeek
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@@ -29,34 +25,12 @@ class NotificationProfileTablesTest {
|
||||
@get:Rule
|
||||
val appDependencies = MockAppDependenciesRule()
|
||||
|
||||
private lateinit var db: SQLiteDatabase
|
||||
private lateinit var database: NotificationProfileTables
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
val sqlCipher = TestDatabaseUtil.inMemoryDatabase {
|
||||
NotificationProfileTables.CREATE_TABLE.forEach {
|
||||
println(it)
|
||||
this.execSQL(it)
|
||||
}
|
||||
NotificationProfileTables.CREATE_INDEXES.forEach {
|
||||
println(it)
|
||||
this.execSQL(it)
|
||||
}
|
||||
}
|
||||
|
||||
db = sqlCipher.myWritableDatabase
|
||||
database = NotificationProfileTables(ApplicationProvider.getApplicationContext(), sqlCipher)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
db.close()
|
||||
}
|
||||
@get:Rule
|
||||
val signalDatabaseRule = SignalDatabaseRule()
|
||||
|
||||
@Test
|
||||
fun `addProfile for profile with empty schedule and members`() {
|
||||
val profile: NotificationProfile = database.createProfile(
|
||||
val profile: NotificationProfile = SignalDatabase.notificationProfiles.createProfile(
|
||||
name = "Profile",
|
||||
emoji = "avatar",
|
||||
color = AvatarColor.A210,
|
||||
@@ -69,7 +43,7 @@ class NotificationProfileTablesTest {
|
||||
assertThat(profile.createdAt).isEqualTo(1000L)
|
||||
assertThat(profile.schedule.id).isEqualTo(1)
|
||||
|
||||
val profiles = database.getProfiles()
|
||||
val profiles = SignalDatabase.notificationProfiles.getProfiles()
|
||||
|
||||
assertThat(profiles)
|
||||
.single()
|
||||
@@ -84,14 +58,14 @@ class NotificationProfileTablesTest {
|
||||
|
||||
@Test
|
||||
fun `updateProfile changes all updateable fields`() {
|
||||
val profile: NotificationProfile = database.createProfile(
|
||||
val profile: NotificationProfile = SignalDatabase.notificationProfiles.createProfile(
|
||||
name = "Profile",
|
||||
emoji = "avatar",
|
||||
color = AvatarColor.A210,
|
||||
createdAt = 1000L
|
||||
).profile
|
||||
|
||||
val updatedProfile = database.updateProfile(
|
||||
val updatedProfile = SignalDatabase.notificationProfiles.updateProfile(
|
||||
profile.copy(
|
||||
name = "Profile 2",
|
||||
emoji = "avatar 2",
|
||||
@@ -109,7 +83,7 @@ class NotificationProfileTablesTest {
|
||||
|
||||
@Test
|
||||
fun `when allowed recipients change profile changes`() {
|
||||
val profile: NotificationProfile = database.createProfile(
|
||||
val profile: NotificationProfile = SignalDatabase.notificationProfiles.createProfile(
|
||||
name = "Profile",
|
||||
emoji = "avatar",
|
||||
color = AvatarColor.A210,
|
||||
@@ -117,22 +91,22 @@ class NotificationProfileTablesTest {
|
||||
).profile
|
||||
assertThat(profile.isRecipientAllowed(RecipientId.from(1))).isFalse()
|
||||
|
||||
var updated = database.addAllowedRecipient(profile.id, RecipientId.from(1))
|
||||
var updated = SignalDatabase.notificationProfiles.addAllowedRecipient(profile.id, RecipientId.from(1))
|
||||
assertThat(updated.isRecipientAllowed(RecipientId.from(1))).isTrue()
|
||||
|
||||
updated = database.removeAllowedRecipient(profile.id, RecipientId.from(1))
|
||||
updated = SignalDatabase.notificationProfiles.removeAllowedRecipient(profile.id, RecipientId.from(1))
|
||||
assertThat(updated.isRecipientAllowed(RecipientId.from(1))).isFalse()
|
||||
|
||||
updated = database.updateProfile(updated.copy(allowedMembers = setOf(RecipientId.from(1)))).profile
|
||||
updated = SignalDatabase.notificationProfiles.updateProfile(updated.copy(allowedMembers = setOf(RecipientId.from(1)))).profile
|
||||
assertThat(updated.isRecipientAllowed(RecipientId.from(1))).isTrue()
|
||||
|
||||
updated = database.updateProfile(updated.copy(allowedMembers = emptySet())).profile
|
||||
updated = SignalDatabase.notificationProfiles.updateProfile(updated.copy(allowedMembers = emptySet())).profile
|
||||
assertThat(updated.isRecipientAllowed(RecipientId.from(1))).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when schedule change profile changes`() {
|
||||
val profile: NotificationProfile = database.createProfile(
|
||||
val profile: NotificationProfile = SignalDatabase.notificationProfiles.createProfile(
|
||||
name = "Profile",
|
||||
emoji = "avatar",
|
||||
color = AvatarColor.A210,
|
||||
@@ -144,7 +118,7 @@ class NotificationProfileTablesTest {
|
||||
assertThat(profile.schedule.daysEnabled, "Contains correct default days")
|
||||
.containsExactlyInAnyOrder(DayOfWeek.MONDAY, DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY, DayOfWeek.THURSDAY, DayOfWeek.FRIDAY)
|
||||
|
||||
database.updateSchedule(
|
||||
SignalDatabase.notificationProfiles.updateSchedule(
|
||||
profile.schedule.copy(
|
||||
enabled = true,
|
||||
start = 800,
|
||||
@@ -152,22 +126,22 @@ class NotificationProfileTablesTest {
|
||||
daysEnabled = setOf(DayOfWeek.SUNDAY, DayOfWeek.FRIDAY)
|
||||
)
|
||||
)
|
||||
var updated = database.getProfile(profile.id)!!
|
||||
var updated = SignalDatabase.notificationProfiles.getProfile(profile.id)!!
|
||||
assertThat(updated.schedule.enabled).isTrue()
|
||||
assertThat(updated.schedule.start).isEqualTo(800)
|
||||
assertThat(updated.schedule.end).isEqualTo(1800)
|
||||
assertThat(updated.schedule.daysEnabled, "Contains updated days days")
|
||||
.containsExactlyInAnyOrder(DayOfWeek.SUNDAY, DayOfWeek.FRIDAY)
|
||||
|
||||
database.updateSchedule(profile.schedule)
|
||||
updated = database.getProfile(profile.id)!!
|
||||
SignalDatabase.notificationProfiles.updateSchedule(profile.schedule)
|
||||
updated = SignalDatabase.notificationProfiles.getProfile(profile.id)!!
|
||||
assertThat(updated.schedule.enabled).isFalse()
|
||||
assertThat(updated.schedule.start).isEqualTo(900)
|
||||
assertThat(updated.schedule.end).isEqualTo(1700)
|
||||
assertThat(updated.schedule.daysEnabled, "Contains correct default days")
|
||||
.containsExactlyInAnyOrder(DayOfWeek.MONDAY, DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY, DayOfWeek.THURSDAY, DayOfWeek.FRIDAY)
|
||||
|
||||
updated = database.updateProfile(
|
||||
updated = SignalDatabase.notificationProfiles.updateProfile(
|
||||
profile.copy(
|
||||
schedule = profile.schedule.copy(
|
||||
enabled = true,
|
||||
@@ -183,7 +157,7 @@ class NotificationProfileTablesTest {
|
||||
assertThat(updated.schedule.daysEnabled, "Contains updated days days")
|
||||
.containsExactlyInAnyOrder(DayOfWeek.SUNDAY, DayOfWeek.FRIDAY)
|
||||
|
||||
updated = database.updateProfile(profile).profile
|
||||
updated = SignalDatabase.notificationProfiles.updateProfile(profile).profile
|
||||
assertThat(updated.schedule.enabled).isFalse()
|
||||
assertThat(updated.schedule.start).isEqualTo(900)
|
||||
assertThat(updated.schedule.end).isEqualTo(1700)
|
||||
|
||||
@@ -1,92 +1,71 @@
|
||||
package org.thoughtcrime.securesms.database
|
||||
|
||||
import android.app.Application
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isFalse
|
||||
import assertk.assertions.isTrue
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.thoughtcrime.securesms.testing.TestDatabaseUtil
|
||||
import android.database.sqlite.SQLiteDatabase as AndroidSQLiteDatabase
|
||||
import org.thoughtcrime.securesms.testutil.SignalDatabaseRule
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(manifest = Config.NONE, application = Application::class)
|
||||
class SmsDatabaseTest {
|
||||
private lateinit var db: AndroidSQLiteDatabase
|
||||
private lateinit var messageTable: MessageTable
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
val sqlCipher = TestDatabaseUtil.inMemoryDatabase {
|
||||
execSQL(MessageTable.CREATE_TABLE)
|
||||
MessageTable.CREATE_INDEXS.forEach {
|
||||
execSQL(it)
|
||||
}
|
||||
}
|
||||
|
||||
db = sqlCipher.myWritableDatabase
|
||||
messageTable = MessageTable(ApplicationProvider.getApplicationContext(), sqlCipher)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
db.close()
|
||||
}
|
||||
@get:Rule
|
||||
val signalDatabaseRule = SignalDatabaseRule()
|
||||
|
||||
@Test
|
||||
fun `getThreadIdForMessage when no message absent for id, return -1`() {
|
||||
assertThat(messageTable.getThreadIdForMessage(1)).isEqualTo(-1)
|
||||
assertThat(SignalDatabase.messages.getThreadIdForMessage(1)).isEqualTo(-1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getThreadIdForMessage when message present for id, return thread id`() {
|
||||
TestSms.insert(db)
|
||||
assertThat(messageTable.getThreadIdForMessage(1)).isEqualTo(1)
|
||||
TestSms.insert(signalDatabaseRule.writeableDatabase)
|
||||
assertThat(SignalDatabase.messages.getThreadIdForMessage(1)).isEqualTo(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hasMeaningfulMessage when no messages, return false`() {
|
||||
assertThat(messageTable.hasMeaningfulMessage(1)).isFalse()
|
||||
assertThat(SignalDatabase.messages.hasMeaningfulMessage(1)).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hasMeaningfulMessage when normal message, return true`() {
|
||||
TestSms.insert(db)
|
||||
assertThat(messageTable.hasMeaningfulMessage(1)).isTrue()
|
||||
TestSms.insert(signalDatabaseRule.writeableDatabase)
|
||||
assertThat(SignalDatabase.messages.hasMeaningfulMessage(1)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hasMeaningfulMessage when GV2 create message only, return true`() {
|
||||
TestSms.insert(db, type = MessageTypes.BASE_INBOX_TYPE or MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.GROUP_V2_BIT or MessageTypes.GROUP_UPDATE_BIT)
|
||||
assertThat(messageTable.hasMeaningfulMessage(1)).isTrue()
|
||||
TestSms.insert(signalDatabaseRule.writeableDatabase, type = MessageTypes.BASE_INBOX_TYPE or MessageTypes.SECURE_MESSAGE_BIT or MessageTypes.GROUP_V2_BIT or MessageTypes.GROUP_UPDATE_BIT)
|
||||
assertThat(SignalDatabase.messages.hasMeaningfulMessage(1)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hasMeaningfulMessage when empty and then with ignored types, always return false`() {
|
||||
assertThat(messageTable.hasMeaningfulMessage(1)).isFalse()
|
||||
assertThat(SignalDatabase.messages.hasMeaningfulMessage(1)).isFalse()
|
||||
|
||||
TestSms.insert(db, type = MessageTypes.IGNORABLE_TYPESMASK_WHEN_COUNTING)
|
||||
assertThat(messageTable.hasMeaningfulMessage(1)).isFalse()
|
||||
TestSms.insert(signalDatabaseRule.writeableDatabase, type = MessageTypes.IGNORABLE_TYPESMASK_WHEN_COUNTING)
|
||||
assertThat(SignalDatabase.messages.hasMeaningfulMessage(1)).isFalse()
|
||||
|
||||
TestSms.insert(db, type = MessageTypes.PROFILE_CHANGE_TYPE)
|
||||
assertThat(messageTable.hasMeaningfulMessage(1)).isFalse()
|
||||
TestSms.insert(signalDatabaseRule.writeableDatabase, type = MessageTypes.PROFILE_CHANGE_TYPE)
|
||||
assertThat(SignalDatabase.messages.hasMeaningfulMessage(1)).isFalse()
|
||||
|
||||
TestSms.insert(db, type = MessageTypes.CHANGE_NUMBER_TYPE)
|
||||
assertThat(messageTable.hasMeaningfulMessage(1)).isFalse()
|
||||
TestSms.insert(signalDatabaseRule.writeableDatabase, type = MessageTypes.CHANGE_NUMBER_TYPE)
|
||||
assertThat(SignalDatabase.messages.hasMeaningfulMessage(1)).isFalse()
|
||||
|
||||
TestSms.insert(db, type = MessageTypes.RELEASE_CHANNEL_DONATION_REQUEST_TYPE)
|
||||
assertThat(messageTable.hasMeaningfulMessage(1)).isFalse()
|
||||
TestSms.insert(signalDatabaseRule.writeableDatabase, type = MessageTypes.RELEASE_CHANNEL_DONATION_REQUEST_TYPE)
|
||||
assertThat(SignalDatabase.messages.hasMeaningfulMessage(1)).isFalse()
|
||||
|
||||
TestSms.insert(db, type = MessageTypes.SMS_EXPORT_TYPE)
|
||||
assertThat(messageTable.hasMeaningfulMessage(1)).isFalse()
|
||||
TestSms.insert(signalDatabaseRule.writeableDatabase, type = MessageTypes.SMS_EXPORT_TYPE)
|
||||
assertThat(SignalDatabase.messages.hasMeaningfulMessage(1)).isFalse()
|
||||
|
||||
TestSms.insert(db, type = MessageTypes.BASE_INBOX_TYPE or MessageTypes.GROUP_V2_LEAVE_BITS)
|
||||
assertThat(messageTable.hasMeaningfulMessage(1)).isFalse()
|
||||
TestSms.insert(signalDatabaseRule.writeableDatabase, type = MessageTypes.BASE_INBOX_TYPE or MessageTypes.GROUP_V2_LEAVE_BITS)
|
||||
assertThat(SignalDatabase.messages.hasMeaningfulMessage(1)).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.database
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import org.thoughtcrime.securesms.database.model.StoryType
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMessage
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
@@ -12,12 +13,14 @@ import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
*/
|
||||
object TestMms {
|
||||
|
||||
private var startSendTimestamp = System.currentTimeMillis()
|
||||
|
||||
fun insert(
|
||||
db: SQLiteDatabase,
|
||||
db: SupportSQLiteDatabase,
|
||||
recipient: Recipient = Recipient.UNKNOWN,
|
||||
recipientId: RecipientId = Recipient.UNKNOWN.id,
|
||||
body: String = "body",
|
||||
sentTimeMillis: Long = System.currentTimeMillis(),
|
||||
sentTimeMillis: Long = startSendTimestamp++,
|
||||
receivedTimestampMillis: Long = System.currentTimeMillis(),
|
||||
expiresIn: Long = 0,
|
||||
expireTimerVersion: Int = 1,
|
||||
@@ -64,7 +67,7 @@ object TestMms {
|
||||
}
|
||||
|
||||
private fun insert(
|
||||
db: SQLiteDatabase,
|
||||
db: SupportSQLiteDatabase,
|
||||
message: OutgoingMessage,
|
||||
recipientId: RecipientId = message.threadRecipient.id,
|
||||
body: String = message.body,
|
||||
@@ -96,7 +99,7 @@ object TestMms {
|
||||
put(MessageTable.MENTIONS_SELF, 0)
|
||||
}
|
||||
|
||||
return db.insert(MessageTable.TABLE_NAME, null, contentValues)
|
||||
return db.insert(MessageTable.TABLE_NAME, 0, contentValues)
|
||||
}
|
||||
|
||||
fun markAsRemoteDelete(db: SQLiteDatabase, messageId: Long) {
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
package org.thoughtcrime.securesms.database
|
||||
|
||||
import android.content.ContentValues
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import org.signal.core.util.orNull
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.mms.IncomingMessage
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import java.util.Optional
|
||||
import java.util.UUID
|
||||
import android.database.sqlite.SQLiteDatabase as AndroidSQLiteDatabase
|
||||
|
||||
/**
|
||||
* Helper methods for inserting SMS messages into the SMS table.
|
||||
*/
|
||||
object TestSms {
|
||||
|
||||
private var startSentTimestamp = System.currentTimeMillis()
|
||||
|
||||
fun insert(
|
||||
db: AndroidSQLiteDatabase,
|
||||
db: SupportSQLiteDatabase,
|
||||
sender: RecipientId = RecipientId.from(1),
|
||||
senderDeviceId: Int = 1,
|
||||
sentTimestampMillis: Long = System.currentTimeMillis(),
|
||||
sentTimestampMillis: Long = startSentTimestamp++,
|
||||
serverTimestampMillis: Long = System.currentTimeMillis(),
|
||||
receivedTimestampMillis: Long = System.currentTimeMillis(),
|
||||
encodedBody: String = "encodedBody",
|
||||
@@ -53,7 +55,7 @@ object TestSms {
|
||||
}
|
||||
|
||||
fun insert(
|
||||
db: AndroidSQLiteDatabase,
|
||||
db: SupportSQLiteDatabase,
|
||||
message: IncomingMessage,
|
||||
type: Long = MessageTypes.BASE_INBOX_TYPE,
|
||||
unread: Boolean = false,
|
||||
@@ -75,6 +77,6 @@ object TestSms {
|
||||
put(MessageTable.SERVER_GUID, message.serverGuid)
|
||||
}
|
||||
|
||||
return db.insert(MessageTable.TABLE_NAME, null, values)
|
||||
return db.insert(MessageTable.TABLE_NAME, 0, values)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
package org.thoughtcrime.securesms.testing
|
||||
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Helper for creating/reading a database for unit tests.
|
||||
*/
|
||||
object TestDatabaseUtil {
|
||||
|
||||
/**
|
||||
* Create an in-memory only database that is empty. Can pass [onCreate] to do similar operations
|
||||
* one would do in a open helper's onCreate.
|
||||
*/
|
||||
fun inMemoryDatabase(onCreate: OnCreate): ProxySQLCipherOpenHelper {
|
||||
val testSQLiteOpenHelper = TestSQLiteOpenHelper(ApplicationProvider.getApplicationContext(), onCreate)
|
||||
return ProxySQLCipherOpenHelper(ApplicationProvider.getApplicationContext(), testSQLiteOpenHelper)
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a database file located in app/src/test/resources/db. Currently only reads
|
||||
* are allowed due to weird caching of the file resulting in non-deterministic tests.
|
||||
*/
|
||||
fun fromFileDatabase(name: String): ProxySQLCipherOpenHelper {
|
||||
val databaseFile = File(javaClass.getResource("/db/$name")!!.file)
|
||||
val sqliteDatabase = SQLiteDatabase.openDatabase(databaseFile.absolutePath, null, SQLiteDatabase.OPEN_READONLY)
|
||||
return ProxySQLCipherOpenHelper(ApplicationProvider.getApplicationContext(), sqliteDatabase, sqliteDatabase)
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package org.thoughtcrime.securesms.testing
|
||||
|
||||
import android.content.Context
|
||||
import android.database.sqlite.SQLiteDatabase as AndroidSQLiteDatabase
|
||||
import android.database.sqlite.SQLiteOpenHelper as AndroidSQLiteOpenHelper
|
||||
|
||||
typealias OnCreate = AndroidSQLiteDatabase.() -> Unit
|
||||
|
||||
/**
|
||||
* [AndroidSQLiteOpenHelper] for use in unit tests.
|
||||
*/
|
||||
class TestSQLiteOpenHelper(context: Context, private val onCreate: OnCreate) : AndroidSQLiteOpenHelper(context, "test", null, 1) {
|
||||
|
||||
fun setup() {
|
||||
onCreate(writableDatabase)
|
||||
}
|
||||
|
||||
override fun onCreate(db: AndroidSQLiteDatabase) {
|
||||
onCreate.invoke(db)
|
||||
}
|
||||
|
||||
override fun onUpgrade(db: AndroidSQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
// no upgrade
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,28 @@
|
||||
package org.thoughtcrime.securesms.testing
|
||||
|
||||
import android.app.Application
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import androidx.sqlite.db.SupportSQLiteOpenHelper
|
||||
import org.thoughtcrime.securesms.crypto.AttachmentSecret
|
||||
import org.thoughtcrime.securesms.crypto.DatabaseSecret
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import java.security.SecureRandom
|
||||
import android.database.sqlite.SQLiteDatabase as AndroidSQLiteDatabase
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase as SQLCipherSQLiteDatabase
|
||||
|
||||
/**
|
||||
* Proxy [SignalDatabase] to the [TestSQLiteOpenHelper] interface.
|
||||
* Test flavor of [SignalDatabase].
|
||||
*/
|
||||
class ProxySQLCipherOpenHelper(
|
||||
class TestSignalDatabase(
|
||||
context: Application,
|
||||
val myReadableDatabase: AndroidSQLiteDatabase,
|
||||
val myWritableDatabase: AndroidSQLiteDatabase
|
||||
val supportReadableDatabase: SupportSQLiteDatabase,
|
||||
val supportWritableDatabase: SupportSQLiteDatabase
|
||||
) : SignalDatabase(context, DatabaseSecret(ByteArray(32).apply { SecureRandom().nextBytes(this) }), AttachmentSecret()) {
|
||||
|
||||
constructor(context: Application, testOpenHelper: TestSQLiteOpenHelper) : this(context, testOpenHelper.readableDatabase, testOpenHelper.writableDatabase)
|
||||
constructor(context: Application, testOpenHelper: SupportSQLiteOpenHelper) : this(context, testOpenHelper.readableDatabase, testOpenHelper.writableDatabase)
|
||||
|
||||
override fun close() {
|
||||
throw UnsupportedOperationException()
|
||||
supportReadableDatabase.close()
|
||||
supportWritableDatabase.close()
|
||||
}
|
||||
|
||||
override val databaseName: String
|
||||
@@ -66,11 +68,13 @@ class ProxySQLCipherOpenHelper(
|
||||
override val rawWritableDatabase: net.zetetic.database.sqlcipher.SQLiteDatabase
|
||||
get() = throw UnsupportedOperationException()
|
||||
|
||||
override val signalReadableDatabase: org.thoughtcrime.securesms.database.SQLiteDatabase
|
||||
get() = ProxySignalSQLiteDatabase(myReadableDatabase)
|
||||
override val signalReadableDatabase: org.thoughtcrime.securesms.database.SQLiteDatabase by lazy {
|
||||
TestSignalSQLiteDatabase(supportReadableDatabase)
|
||||
}
|
||||
|
||||
override val signalWritableDatabase: org.thoughtcrime.securesms.database.SQLiteDatabase
|
||||
get() = ProxySignalSQLiteDatabase(myWritableDatabase)
|
||||
override val signalWritableDatabase: org.thoughtcrime.securesms.database.SQLiteDatabase by lazy {
|
||||
TestSignalSQLiteDatabase(supportWritableDatabase)
|
||||
}
|
||||
|
||||
override fun getSqlCipherDatabase(): SQLCipherSQLiteDatabase {
|
||||
throw UnsupportedOperationException()
|
||||
@@ -2,8 +2,10 @@ package org.thoughtcrime.securesms.testing
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import android.database.SQLException
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import androidx.sqlite.db.SupportSQLiteQuery
|
||||
import org.signal.core.util.toAndroidQuery
|
||||
import net.zetetic.database.sqlcipher.SQLiteQueryBuilder
|
||||
import java.util.Locale
|
||||
import android.database.sqlite.SQLiteDatabase as AndroidSQLiteDatabase
|
||||
import android.database.sqlite.SQLiteTransactionListener as AndroidSQLiteTransactionListener
|
||||
@@ -15,7 +17,7 @@ import org.thoughtcrime.securesms.database.SQLiteDatabase as SignalSQLiteDatabas
|
||||
* Partial implementation of [SignalSQLiteDatabase] using an instance of [AndroidSQLiteDatabase] instead
|
||||
* of SQLCipher.
|
||||
*/
|
||||
class ProxySignalSQLiteDatabase(private val database: AndroidSQLiteDatabase) : SignalSQLiteDatabase(null) {
|
||||
class TestSignalSQLiteDatabase(private val database: SupportSQLiteDatabase) : SignalSQLiteDatabase(null) {
|
||||
override fun getSqlCipherDatabase(): net.zetetic.database.sqlcipher.SQLiteDatabase {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
@@ -33,7 +35,7 @@ class ProxySignalSQLiteDatabase(private val database: AndroidSQLiteDatabase) : S
|
||||
}
|
||||
|
||||
override fun query(distinct: Boolean, table: String, columns: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, groupBy: String?, having: String?, orderBy: String?, limit: String?): Cursor {
|
||||
return database.query(distinct, table, columns, selection, selectionArgs, groupBy, having, orderBy, limit)
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun queryWithFactory(
|
||||
@@ -48,75 +50,83 @@ class ProxySignalSQLiteDatabase(private val database: AndroidSQLiteDatabase) : S
|
||||
orderBy: String?,
|
||||
limit: String?
|
||||
): Cursor {
|
||||
return database.queryWithFactory(null, distinct, table, columns, selection, selectionArgs, groupBy, having, orderBy, limit)
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun query(query: SupportSQLiteQuery): Cursor {
|
||||
val converted = query.toAndroidQuery()
|
||||
return database.rawQuery(converted.where, converted.whereArgs)
|
||||
}
|
||||
|
||||
override fun query(table: String, columns: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, groupBy: String?, having: String?, orderBy: String?): Cursor {
|
||||
return database.query(table, columns, selection, selectionArgs, groupBy, having, orderBy)
|
||||
return database.query(query)
|
||||
}
|
||||
|
||||
override fun query(table: String, columns: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, groupBy: String?, having: String?, orderBy: String?, limit: String?): Cursor {
|
||||
return database.query(table, columns, selection, selectionArgs, groupBy, having, orderBy, limit)
|
||||
val query: String = SQLiteQueryBuilder.buildQueryString(false, table, columns, selection, groupBy, having, orderBy, limit)
|
||||
return database.query(query, selectionArgs ?: emptyArray())
|
||||
}
|
||||
|
||||
override fun query(table: String, columns: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, groupBy: String?, having: String?, orderBy: String?): Cursor {
|
||||
return query(table, columns, selection, selectionArgs, groupBy, having, orderBy, null)
|
||||
}
|
||||
|
||||
override fun rawQuery(sql: String, selectionArgs: Array<out String>?): Cursor {
|
||||
return database.rawQuery(sql, selectionArgs)
|
||||
return database.query(sql, selectionArgs ?: emptyArray())
|
||||
}
|
||||
|
||||
override fun rawQuery(sql: String, args: Array<out Any>?): Cursor {
|
||||
return database.rawQuery(sql, args?.map(Any::toString)?.toTypedArray())
|
||||
return database.query(sql, args ?: emptyArray())
|
||||
}
|
||||
|
||||
override fun rawQueryWithFactory(cursorFactory: net.zetetic.database.sqlcipher.SQLiteDatabase.CursorFactory?, sql: String, selectionArgs: Array<out String>?, editTable: String): Cursor {
|
||||
return database.rawQueryWithFactory(null, sql, selectionArgs, editTable)
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun rawQuery(sql: String, selectionArgs: Array<out String>?, initialRead: Int, maxRead: Int): Cursor {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun insert(table: String, nullColumnHack: String?, values: ContentValues?): Long {
|
||||
return database.insert(table, nullColumnHack, values)
|
||||
override fun insert(table: String, nullColumnHack: String?, values: ContentValues): Long {
|
||||
return database.insert(table, 0, values)
|
||||
}
|
||||
|
||||
override fun insertOrThrow(table: String, nullColumnHack: String?, values: ContentValues?): Long {
|
||||
return database.insertOrThrow(table, nullColumnHack, values)
|
||||
override fun insertOrThrow(table: String, nullColumnHack: String?, values: ContentValues): Long {
|
||||
val result = database.insert(table, 0, values)
|
||||
if (result < 0) {
|
||||
throw SQLException()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun replace(table: String, nullColumnHack: String?, initialValues: ContentValues?): Long {
|
||||
return database.replace(table, nullColumnHack, initialValues)
|
||||
override fun replace(table: String, nullColumnHack: String?, initialValues: ContentValues): Long {
|
||||
return database.insert(table, 5, initialValues)
|
||||
}
|
||||
|
||||
override fun replaceOrThrow(table: String, nullColumnHack: String?, initialValues: ContentValues?): Long {
|
||||
return database.replaceOrThrow(table, nullColumnHack, initialValues)
|
||||
override fun replaceOrThrow(table: String, nullColumnHack: String?, initialValues: ContentValues): Long {
|
||||
val result = replace(table, nullColumnHack, initialValues)
|
||||
if (result < 0) {
|
||||
throw SQLException()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun insertWithOnConflict(table: String, nullColumnHack: String?, initialValues: ContentValues?, conflictAlgorithm: Int): Long {
|
||||
return database.insertWithOnConflict(table, nullColumnHack, initialValues, conflictAlgorithm)
|
||||
override fun insertWithOnConflict(table: String, nullColumnHack: String?, initialValues: ContentValues, conflictAlgorithm: Int): Long {
|
||||
return database.insert(table, conflictAlgorithm, initialValues)
|
||||
}
|
||||
|
||||
override fun delete(table: String, whereClause: String?, whereArgs: Array<out String>?): Int {
|
||||
return database.delete(table, whereClause, whereArgs)
|
||||
}
|
||||
|
||||
override fun update(table: String, values: ContentValues?, whereClause: String?, whereArgs: Array<out String>?): Int {
|
||||
return database.update(table, values, whereClause, whereArgs)
|
||||
override fun update(table: String, values: ContentValues, whereClause: String?, whereArgs: Array<out String>?): Int {
|
||||
return database.update(table, 0, values, whereClause, whereArgs)
|
||||
}
|
||||
|
||||
override fun updateWithOnConflict(table: String, values: ContentValues?, whereClause: String?, whereArgs: Array<out String>?, conflictAlgorithm: Int): Int {
|
||||
return database.updateWithOnConflict(table, values, whereClause, whereArgs, conflictAlgorithm)
|
||||
override fun updateWithOnConflict(table: String, values: ContentValues, whereClause: String?, whereArgs: Array<out String>?, conflictAlgorithm: Int): Int {
|
||||
return database.update(table, conflictAlgorithm, values, whereClause, whereArgs)
|
||||
}
|
||||
|
||||
override fun execSQL(sql: String) {
|
||||
database.execSQL(sql)
|
||||
}
|
||||
|
||||
override fun rawExecSQL(sql: String?) {
|
||||
override fun rawExecSQL(sql: String) {
|
||||
database.execSQL(sql)
|
||||
}
|
||||
|
||||
@@ -135,8 +145,8 @@ class ProxySignalSQLiteDatabase(private val database: AndroidSQLiteDatabase) : S
|
||||
override val isWriteAheadLoggingEnabled: Boolean
|
||||
get() = throw UnsupportedOperationException()
|
||||
|
||||
override fun setForeignKeyConstraintsEnabled(enable: Boolean) {
|
||||
database.setForeignKeyConstraintsEnabled(enable)
|
||||
override fun setForeignKeyConstraintsEnabled(enabled: Boolean) {
|
||||
database.setForeignKeyConstraintsEnabled(enabled)
|
||||
}
|
||||
|
||||
override fun beginTransactionWithListener(transactionListener: SQLCipherSQLiteTransactionListener?) {
|
||||
@@ -182,23 +192,22 @@ class ProxySignalSQLiteDatabase(private val database: AndroidSQLiteDatabase) : S
|
||||
override val isDbLockedByCurrentThread: Boolean
|
||||
get() = database.isDbLockedByCurrentThread
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun isDbLockedByOtherThreads(): Boolean {
|
||||
return database.isDbLockedByOtherThreads
|
||||
return false
|
||||
}
|
||||
|
||||
override fun yieldIfContendedSafely(): Boolean {
|
||||
return database.yieldIfContendedSafely()
|
||||
}
|
||||
|
||||
override fun yieldIfContendedSafely(sleepAfterYieldDelay: Long): Boolean {
|
||||
return database.yieldIfContendedSafely(sleepAfterYieldDelay)
|
||||
override fun yieldIfContendedSafely(sleepAfterYieldDelayMillis: Long): Boolean {
|
||||
return database.yieldIfContendedSafely(sleepAfterYieldDelayMillis)
|
||||
}
|
||||
|
||||
override var version: Int
|
||||
get() = database.version
|
||||
set(value) {
|
||||
database.version = version
|
||||
database.version = value
|
||||
}
|
||||
|
||||
override val maximumSize: Long
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.testutil
|
||||
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import androidx.sqlite.db.SupportSQLiteOpenHelper
|
||||
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import io.mockk.every
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.unmockkObject
|
||||
import org.junit.rules.ExternalResource
|
||||
import org.thoughtcrime.securesms.database.SQLiteDatabase
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.testing.TestSignalDatabase
|
||||
|
||||
class SignalDatabaseRule : ExternalResource() {
|
||||
|
||||
lateinit var signalDatabase: TestSignalDatabase
|
||||
|
||||
val readableDatabase: SQLiteDatabase
|
||||
get() = signalDatabase.signalReadableDatabase
|
||||
|
||||
val writeableDatabase: SQLiteDatabase
|
||||
get() = signalDatabase.signalWritableDatabase
|
||||
|
||||
override fun before() {
|
||||
signalDatabase = inMemorySignalDatabase()
|
||||
|
||||
mockkObject(SignalDatabase)
|
||||
every { SignalDatabase.instance } returns signalDatabase
|
||||
}
|
||||
|
||||
override fun after() {
|
||||
unmockkObject(SignalDatabase)
|
||||
signalDatabase.close()
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Create an in-memory only database mimicking one created fresh for Signal. This includes
|
||||
* all non-FTS tables, indexes, and triggers.
|
||||
*/
|
||||
private fun inMemorySignalDatabase(): TestSignalDatabase {
|
||||
val configuration = SupportSQLiteOpenHelper.Configuration(
|
||||
context = ApplicationProvider.getApplicationContext(),
|
||||
name = "test",
|
||||
callback = object : SupportSQLiteOpenHelper.Callback(1) {
|
||||
override fun onCreate(db: SupportSQLiteDatabase) = Unit
|
||||
override fun onUpgrade(db: SupportSQLiteDatabase, oldVersion: Int, newVersion: Int) = Unit
|
||||
},
|
||||
useNoBackupDirectory = false,
|
||||
allowDataLossOnRecovery = true
|
||||
)
|
||||
|
||||
val helper = FrameworkSQLiteOpenHelperFactory().create(configuration)
|
||||
val signalDatabase = TestSignalDatabase(ApplicationProvider.getApplicationContext(), helper)
|
||||
signalDatabase.onCreateTablesIndexesAndTriggers(signalDatabase.signalWritableDatabase)
|
||||
|
||||
return signalDatabase
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user