Skip to content

Commit

Permalink
Changes to StandardMessage test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
sashaweiss-signal authored Sep 16, 2024
1 parent 1cb32a8 commit 60447fb
Show file tree
Hide file tree
Showing 207 changed files with 1,350 additions and 921 deletions.
65 changes: 59 additions & 6 deletions src/main/kotlin/Permutations.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@

import com.squareup.wire.Message
import okio.ByteString.Companion.toByteString
import org.signal.libsignal.zkgroup.ServerSecretParams
import org.signal.libsignal.zkgroup.receipts.ClientZkReceiptOperations
import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialPresentation
import org.signal.libsignal.zkgroup.receipts.ReceiptSerial
import org.signal.libsignal.zkgroup.receipts.ServerZkReceiptOperations
import org.thoughtcrime.securesms.backup.v2.proto.*
import java.security.SecureRandom
import java.util.*
import kotlin.math.max

Expand Down Expand Up @@ -163,10 +169,6 @@ class PermutationScope : Iterator<List<Message<*, *>>> {
fun <T> someEnum(clazz: Class<T>, excluding: T? = null): T = excluding?.let { some(Generators.enum(clazz, excluding)) } ?: some(Generators.enum(clazz))
fun <T> someEnum(clazz: Class<T>, vararg excluding: T): T = some(Generators.enum(clazz, *excluding))

fun <T> someNullableEnum(clazz: Class<T>): T? = some(Generators.enum(clazz).nullable())

fun someNullableFilePointer(): FilePointer? = some(Generators.filePointer().nullable())

fun someCallLinkRootKey(): ByteArray = some(Generators.callLinkRootKey())

fun <T> some(generator: Generator<T>): T {
Expand Down Expand Up @@ -308,6 +310,22 @@ object Generators {
*/
fun expirationTimersMs(): Generator<Long> = Generators.list(0L, SeededRandom.long(0, Int.MAX_VALUE.toLong()) * 1000, SeededRandom.long(0, Int.MAX_VALUE.toLong()) * 1000)

fun receiptCredentialPresentation(): Generator<ReceiptCredentialPresentation> {
// libsignal requires a SecureRandom for zkgroup operations
val derivedRandom = SecureRandom.getInstance("SHA1PRNG")
derivedRandom.setSeed(SeededRandom.long())
val serverParams = ServerSecretParams.generate(derivedRandom)
val clientOps = ClientZkReceiptOperations(serverParams.publicParams)

val serial = ReceiptSerial(SeededRandom.bytes(ReceiptSerial.SIZE))
val context = clientOps.createReceiptCredentialRequestContext(derivedRandom, serial)
val response = ServerZkReceiptOperations(serverParams).issueReceiptCredential(derivedRandom, context.request, 0, 1)
val credential = clientOps.receiveReceiptCredential(context, response)
val presentation = clientOps.createReceiptCredentialPresentation(derivedRandom, credential)

return presentation.asGenerator()
}

fun <T> list(vararg items: T): Generator<T> = ListGenerator(items.toList())
fun <T> list(items: List<T>): Generator<T> = ListGenerator(items)
fun <T> single(item: T): Generator<T> = Generators.list(item)
Expand All @@ -333,29 +351,65 @@ object Generators {
includeFileName = false,
includeMediaSize = false,
includeCaption = false,
includeBlurHash = true,
contentTypeGenerator = Generators.list("image/jpeg", "image/png")
)

fun avatarFilePointer(): Generator<FilePointer> = filePointerInternal(
includeFileName = true,
includeMediaSize = true,
includeCaption = false,
includeBlurHash = true,
contentTypeGenerator = Generators.list("image/jpeg", "image/png")
)

fun longTextFilePointer(): Generator<FilePointer> = filePointerInternal(
includeFileName = true,
includeMediaSize = false,
includeCaption = false,
includeBlurHash = false,
contentTypeGenerator = Generators.list("text/x-signal-plain")
)

fun voiceMessageFilePointer(): Generator<FilePointer> = filePointerInternal(
includeFileName = true,
includeMediaSize = false,
includeCaption = false,
includeBlurHash = false,
contentTypeGenerator = Generators.list("audio/mp3")
)

fun gifFilePointer(): Generator<FilePointer> = filePointerInternal(
includeFileName = true,
includeMediaSize = true,
includeCaption = false,
includeBlurHash = true,
contentTypeGenerator = Generators.list("video/mp4")
)

fun borderlessFilePointer(): Generator<FilePointer> = filePointerInternal(
includeFileName = true,
includeMediaSize = true,
includeCaption = false,
includeBlurHash = true,
contentTypeGenerator = Generators.list("image/png")
)

fun filePointer(
contentTypeGenerator: Generator<String> = Generators.list("image/jpeg", "image/png", "image/gif", "audio/mp3", "video/mp4")
): Generator<FilePointer> = filePointerInternal(
includeFileName = true,
includeMediaSize = true,
includeCaption = true,
includeBlurHash = true,
contentTypeGenerator = contentTypeGenerator
)

private fun filePointerInternal(
includeFileName: Boolean,
includeMediaSize: Boolean,
includeCaption: Boolean,
includeBlurHash: Boolean,
contentTypeGenerator: Generator<String>
): Generator<FilePointer> {
val (backupLocatorGenerator, attachmentLocatorGenerator, invalidAttachmentLocatorGenerator) = oneOf(
Expand Down Expand Up @@ -400,7 +454,6 @@ object Generators {
val incrementalMacChunkSize: Int? = some(Generators.list(1024, 2048))

val contentType = some(contentTypeGenerator)
val blurHash = some(Generators.blurHashes().nullable())

frames += FilePointer(
backupLocator = backupLocator,
Expand All @@ -417,7 +470,7 @@ object Generators {
width = if (includeMediaSize) some(Generators.ints(0, 4096).nullable()) else null,
height = if (includeMediaSize) some(Generators.ints(0, 4096).nullable()) else null,
caption = if (includeCaption) someNullableString() else null,
blurHash = if (contentType.startsWith("audio")) null else blurHash
blurHash = if (includeBlurHash) some(Generators.blurHashes().nullable()) else null
)
}
}
Expand Down
25 changes: 1 addition & 24 deletions src/main/kotlin/tests/ChatItemGiftBadgeTestCase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,16 @@ package tests

import Generators
import PermutationScope
import SeededRandom
import StandardFrames
import TestCase
import okio.ByteString
import okio.ByteString.Companion.toByteString
import org.signal.libsignal.zkgroup.ServerSecretParams
import org.signal.libsignal.zkgroup.receipts.ClientZkReceiptOperations
import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialPresentation
import org.signal.libsignal.zkgroup.receipts.ReceiptSerial
import org.signal.libsignal.zkgroup.receipts.ServerZkReceiptOperations
import org.thoughtcrime.securesms.backup.v2.proto.*
import java.security.SecureRandom

/**
* Incoming/outgoing gift badges
*/
object ChatItemGiftBadgeTestCase : TestCase("chat_item_gift_badge") {
private val validPresentation: ReceiptCredentialPresentation = run {
// libsignal requires a SecureRandom for zkgroup operations
val derivedRandom = SecureRandom.getInstance("SHA1PRNG")
derivedRandom.setSeed(SeededRandom.long())
val serverParams = ServerSecretParams.generate(derivedRandom)
val clientOps = ClientZkReceiptOperations(serverParams.publicParams)

val serial = ReceiptSerial(SeededRandom.bytes(ReceiptSerial.SIZE))
val context = clientOps.createReceiptCredentialRequestContext(derivedRandom, serial)
val response = ServerZkReceiptOperations(serverParams).issueReceiptCredential(derivedRandom, context.request, 0, 1)
val credential = clientOps.receiveReceiptCredential(context, response)
val presentation = clientOps.createReceiptCredentialPresentation(derivedRandom, credential)

presentation
}

override fun PermutationScope.execute() {
frames += StandardFrames.MANDATORY_FRAMES

Expand Down Expand Up @@ -66,7 +43,7 @@ object ChatItemGiftBadgeTestCase : TestCase("chat_item_gift_badge") {
receiptCredentialPresentation = if (state == GiftBadge.State.FAILED) {
ByteString.EMPTY
} else {
validPresentation.serialize().toByteString()
some(Generators.receiptCredentialPresentation()).serialize().toByteString()
}
)
)
Expand Down
112 changes: 89 additions & 23 deletions src/main/kotlin/tests/ChatItemStandardMessageFormattedTextTestCase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ package tests
import Generator
import Generators
import PermutationScope
import StandardFrames
import TestCase
import asList
import okio.ByteString
import oneOf
import org.thoughtcrime.securesms.backup.v2.proto.*

Expand All @@ -31,37 +33,101 @@ object ChatItemStandardMessageFormattedTextTestCase : TestCase("chat_item_standa
val incoming = some(incomingGenerator)
val outgoing = some(outgoingGenerator)

fun makeChatItemFrame(bodyRanges: List<BodyRange>): Frame {
return Frame(
chatItem = ChatItem(
chatId = StandardFrames.chatGroupAB.chat!!.id,
authorId = if (outgoing != null) {
StandardFrames.recipientSelf.recipient!!.id
} else {
StandardFrames.recipientAlice.recipient!!.id
},
dateSent = someIncrementingTimestamp(),
incoming = incoming,
outgoing = outgoing,
standardMessage = StandardMessage(
text = Text(
body = "0123456789",
bodyRanges = bodyRanges
)
)
)
)
}

fun makeStyleGenerator(): Generator<BodyRange.Style> {
return Generators.enum(BodyRange.Style::class.java, excluding = BodyRange.Style.NONE)
}

val (mentionGenerator, styleGenerator) = oneOf(
Generators.list(
StandardFrames.recipientAlice.recipient.contact!!.aci,
StandardFrames.recipientBob.recipient.contact!!.aci
),
Generators.enum(BodyRange.Style::class.java) as Generator<Any?>
makeStyleGenerator() as Generator<Any?>
)

// Mentions and styles can occupy identical or disjoint ranges, but cannot
// partially overlap. (I.e., styles cannot be applied to part-but-not-all
// of a mention.)
val (mentionRangeGenerator, styleRangeGenerator) = listOf(
Generators.list(
Pair(6, 2),
Pair(8, 2)
),
// Includes intentional doubles, so we get multiple styles with the same
// exact ranges.
Generators.list(
Pair(0, 1),
Pair(0, 4),
Pair(0, 4),
Pair(1, 5),
Pair(2, 2),
Pair(6, 2),
Pair(6, 2)
)
)

frames += Frame(
chatItem = ChatItem(
chatId = StandardFrames.chatGroupAB.chat!!.id,
authorId = if (outgoing != null) {
StandardFrames.recipientSelf.recipient!!.id
frames += makeChatItemFrame(
bodyRanges = Generators.permutation<BodyRange> {
val style: BodyRange.Style? = someOneOf(styleGenerator)
val styleRange = some(styleRangeGenerator)

val mentionRange = some(mentionRangeGenerator)
val mention: ByteString? = someOneOf(mentionGenerator)

val range: Pair<Int, Int> = if (style != null) {
styleRange
} else {
StandardFrames.recipientAlice.recipient!!.id
},
dateSent = someIncrementingTimestamp(),
incoming = incoming,
outgoing = outgoing,
standardMessage = StandardMessage(
text = Text(
body = "0123456789",
bodyRanges = Generators.permutation<BodyRange> {
frames += BodyRange(
start = some(Generators.list(0, 0, 0, 1, 2, 3)),
length = some(Generators.list(1, 5, 10, 5, 7, 3)),
style = someOneOf(styleGenerator),
mentionAci = someOneOf(mentionGenerator)
)
}.asList(0, 1, 2, 3, 4, 5).let { some(it) }
)
mentionRange
}

frames += BodyRange(
start = range.first,
length = range.second,
style = style,
mentionAci = mention
)
}.asList(0, 1, 2, 3, 4, 5).let { some(it) }
)

// Add a special frame where the style and the mention are both the full
// length of the body. We can't cover this case with the ranges in the frame
// above, since style/mention ranges can't ever partially overlap; if there
// were a style/mention range with a full-length range in the frame above,
// it'd potentially overlap with another style/mention range in the same
// frame.
frames += makeChatItemFrame(
bodyRanges = listOf(
BodyRange(
start = 0,
length = 10,
style = some(makeStyleGenerator())
),
BodyRange(
start = 0,
length = 10,
mentionAci = StandardFrames.recipientAlice.recipient.contact.aci
)
)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package tests

import Generators
import PermutationScope
import TestCase
import org.thoughtcrime.securesms.backup.v2.proto.ChatItem
Expand Down Expand Up @@ -36,9 +37,10 @@ object ChatItemStandardMessageLongTextTestCase : TestCase("chat_item_standard_me
outgoing = outgoing,
standardMessage = StandardMessage(
text = Text(
body = someString()
// Long text must have a non-empty message body.
body = someNonEmptyString()
),
longText = someNullableFilePointer(),
longText = some(Generators.longTextFilePointer()),
reactions = some(Generators.reactions(2, StandardFrames.recipientSelf.recipient!!, StandardFrames.recipientAlice.recipient))
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ object ChatItemStandardMessageSpecialAttachmentsTestCase : TestCase("chat_item_s
val incoming = some(incomingGenerator)
val outgoing = some(outgoingGenerator)

val voiceMessageFilePointer = some(Generators.voiceMessageFilePointer())
val gifFilePointer = some(Generators.gifFilePointer())
val borderlessFilePointer = some(Generators.borderlessFilePointer())

frames += Frame(
chatItem = ChatItem(
chatId = StandardFrames.chatAlice.chat!!.id,
Expand All @@ -38,19 +42,16 @@ object ChatItemStandardMessageSpecialAttachmentsTestCase : TestCase("chat_item_s
standardMessage = StandardMessage(
quote = null,
attachments = Generators.permutation<MessageAttachment> {
val flag = some(Generators.list(MessageAttachment.Flag.VOICE_MESSAGE, MessageAttachment.Flag.BORDERLESS, MessageAttachment.Flag.GIF))
val pointer = some(Generators.filePointer())
val flag = someEnum(MessageAttachment.Flag::class.java, excluding = MessageAttachment.Flag.NONE)

frames += MessageAttachment(
flag = flag,
pointer = if (flag == MessageAttachment.Flag.VOICE_MESSAGE) {
pointer.copy(contentType = "audio/mp3", blurHash = null)
} else if (flag == MessageAttachment.Flag.GIF) {
pointer.copy(contentType = "video/mp4")
} else if (flag == MessageAttachment.Flag.BORDERLESS) {
pointer.copy(contentType = "image/png")
} else {
pointer
pointer = when (flag) {
MessageAttachment.Flag.VOICE_MESSAGE -> voiceMessageFilePointer
MessageAttachment.Flag.GIF -> gifFilePointer
MessageAttachment.Flag.BORDERLESS -> borderlessFilePointer
// Excluded above.
MessageAttachment.Flag.NONE -> throw NotImplementedError()
},
wasDownloaded = someBoolean()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ object ChatItemStandardMessageStandardAttachmentsTestCase : TestCase("chat_item_
quote = null,
text = someNullablePermutation {
frames += Text(
body = someString()
body = someNonEmptyString()
)
},
attachments = Generators.permutation<MessageAttachment> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ object ChatItemStandardMessageTextOnlyTestCase : TestCase("chat_item_standard_me
outgoing = outgoing,
standardMessage = StandardMessage(
text = Text(
body = someString()
body = someNonEmptyString()
),
reactions = some(Generators.reactions(2, StandardFrames.recipientSelf.recipient!!, StandardFrames.recipientAlice.recipient))
)
Expand Down
Loading

0 comments on commit 60447fb

Please sign in to comment.