From 36ef36be6124e314f3f4fc5e8e209d0798dd3eb4 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Tue, 11 Apr 2023 11:52:38 -0400 Subject: [PATCH] Cleanup MessageContentProcessorTestV2. --- .../messages/MessageContentProcessorTestV2.kt | 47 ++++----- .../securesms/testing/MessageContentFuzzer.kt | 96 +++++++++++-------- .../messages/DataMessageProcessor.kt | 2 +- .../messages/MessageContentProcessor.java | 2 +- 4 files changed, 78 insertions(+), 69 deletions(-) diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageContentProcessorTestV2.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageContentProcessorTestV2.kt index c8150374f7..c566b645a9 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageContentProcessorTestV2.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageContentProcessorTestV2.kt @@ -8,6 +8,7 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.signal.core.util.ThreadUtil +import org.signal.core.util.logging.Log import org.signal.core.util.readToList import org.signal.core.util.select import org.signal.core.util.toSingleLine @@ -38,9 +39,10 @@ import java.util.Optional class MessageContentProcessorTestV2 { companion object { - private val TAGS = listOf(MessageContentProcessorV2.TAG, AttachmentTable.TAG) + private val TAGS = listOf(MessageContentProcessor.TAG, MessageContentProcessorV2.TAG, AttachmentTable.TAG) private val GENERALIZE_TAG = mapOf( + MessageContentProcessor.TAG to "MCP", MessageContentProcessorV2.TAG to "MCP", AttachmentTable.TAG to AttachmentTable.TAG ) @@ -78,12 +80,12 @@ class MessageContentProcessorTestV2 { fun textMessage() { var start = envelopeTimestamp - val messages: List = (0 until 1).map { + val messages: List = (0 until 100).map { start += 200 TestMessage( envelope = MessageContentFuzzer.envelope(start), content = MessageContentFuzzer.fuzzTextMessage(), - metadata = MessageContentFuzzer.fuzzMetadata(harness.others[0], harness.self.id), + metadata = MessageContentFuzzer.envelopeMetadata(harness.others[0], harness.self.id), serverDeliveredTimestamp = MessageContentFuzzer.fuzzServerDeliveredTimestamp(start) ) } @@ -98,59 +100,52 @@ class MessageContentProcessorTestV2 { fun mediaMessage() { var start = envelopeTimestamp - val messages: List = (0 until 10).map { + val textMessages: List = (0 until 10).map { start += 200 TestMessage( envelope = MessageContentFuzzer.envelope(start), content = MessageContentFuzzer.fuzzTextMessage(), - metadata = MessageContentFuzzer.fuzzMetadata(harness.others[0], harness.self.id), + metadata = MessageContentFuzzer.envelopeMetadata(harness.others[0], harness.self.id), serverDeliveredTimestamp = MessageContentFuzzer.fuzzServerDeliveredTimestamp(start) ) } - val moreMessages: List = (0 until 10).map { + val firstBatchMediaMessages: List = (0 until 10).map { start += 200 TestMessage( envelope = MessageContentFuzzer.envelope(start), - content = MessageContentFuzzer.fuzzMediaMessageWithBody(messages), - metadata = MessageContentFuzzer.fuzzMetadata(harness.others[0], harness.self.id), + content = MessageContentFuzzer.fuzzMediaMessageWithBody(textMessages), + metadata = MessageContentFuzzer.envelopeMetadata(harness.others[0], harness.self.id), serverDeliveredTimestamp = MessageContentFuzzer.fuzzServerDeliveredTimestamp(start) ) } - val evenMoreMessages: List = (0 until 10).map { + val secondBatchNoContentMediaMessages: List = (0 until 10).map { start += 200 TestMessage( envelope = MessageContentFuzzer.envelope(start), - content = MessageContentFuzzer.fuzzMediaMessageNoContent(messages + moreMessages), - metadata = MessageContentFuzzer.fuzzMetadata(harness.others[0], harness.self.id), + content = MessageContentFuzzer.fuzzMediaMessageNoContent(textMessages + firstBatchMediaMessages), + metadata = MessageContentFuzzer.envelopeMetadata(harness.others[0], harness.self.id), serverDeliveredTimestamp = MessageContentFuzzer.fuzzServerDeliveredTimestamp(start) ) } - val evenMoreMoreMessages: List = (0 until 10).map { + val thirdBatchNoTextMediaMessagesMessages: List = (0 until 10).map { start += 200 TestMessage( envelope = MessageContentFuzzer.envelope(start), - content = MessageContentFuzzer.fuzzMediaMessageNoText(messages + moreMessages), - metadata = MessageContentFuzzer.fuzzMetadata(harness.others[0], harness.self.id), + content = MessageContentFuzzer.fuzzMediaMessageNoText(textMessages + firstBatchMediaMessages), + metadata = MessageContentFuzzer.envelopeMetadata(harness.others[0], harness.self.id), serverDeliveredTimestamp = MessageContentFuzzer.fuzzServerDeliveredTimestamp(start) ) } - testResult.runV2(messages + moreMessages + evenMoreMessages + evenMoreMoreMessages) - testResult.runV1(messages + moreMessages + evenMoreMessages + evenMoreMoreMessages) + testResult.runV2(textMessages + firstBatchMediaMessages + secondBatchNoContentMediaMessages + thirdBatchNoTextMediaMessagesMessages) + testResult.runV1(textMessages + firstBatchMediaMessages + secondBatchNoContentMediaMessages + thirdBatchNoTextMediaMessagesMessages) testResult.assert() } -// @Test -// fun fuzzIt() { -// MessageContentFuzzer.fuzzProto(SignalServiceProtos.DataMessage.Contact.Name::class) -// MessageContentFuzzer.fuzzProto(SignalServiceProtos.DataMessage.Contact.Avatar::class) -// MessageContentFuzzer.fuzzProto(SignalServiceProtos.DataMessage.Contact.Email::class) -// } - private inner class TestResults { private lateinit var v1Logs: List @@ -285,8 +280,8 @@ class MessageContentProcessorTestV2 { Cursor.FIELD_TYPE_BLOB -> Base64.encodeToString(cursor.getBlob(index), 0) else -> cursor.getString(index) } - if (table == MessageTable.TABLE_NAME && column == "type") { - data = thing(cursor.getLong(index)) + if (table == MessageTable.TABLE_NAME && column == MessageTable.TYPE) { + data = typeColumnToString(cursor.getLong(index)) } column to data @@ -319,7 +314,7 @@ class MessageContentProcessorTestV2 { return SignalServiceContent.createFromProto(contentProto)!! } - fun thing(type: Long): String { + fun typeColumnToString(type: Long): String { return """ isOutgoingMessageType:${isOutgoingMessageType(type)} isForcedSms:${type and MessageTypes.MESSAGE_FORCE_SMS_BIT != 0L} diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/MessageContentFuzzer.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/MessageContentFuzzer.kt index a3d29a8ecf..f471145476 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/MessageContentFuzzer.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/MessageContentFuzzer.kt @@ -1,7 +1,6 @@ package org.thoughtcrime.securesms.testing import com.google.protobuf.ByteString -import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.database.model.toProtoByteString import org.thoughtcrime.securesms.messages.TestMessage import org.thoughtcrime.securesms.recipients.Recipient @@ -16,13 +15,11 @@ import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelo import java.util.UUID import kotlin.random.Random import kotlin.random.nextInt -import kotlin.reflect.KClass -import kotlin.reflect.KFunction -import kotlin.reflect.full.declaredFunctions -import kotlin.reflect.full.functions -import kotlin.reflect.jvm.jvmErasure import kotlin.time.Duration.Companion.days +/** + * Random but deterministic fuzzer for create various message content protos. + */ object MessageContentFuzzer { private val mediaTypes = listOf("image/png", "image/jpeg", "image/heic", "image/heif", "image/avif", "image/webp", "image/gif", "audio/aac", "audio/*", "video/mp4", "video/*", "text/x-vcard", "text/x-signal-plain", "application/x-signal-view-once", "*/*", "application/octet-stream") @@ -30,6 +27,9 @@ object MessageContentFuzzer { private val random = Random(1) + /** + * Create an [Envelope]. + */ fun envelope(timestamp: Long): Envelope { return Envelope.newBuilder() .setTimestamp(timestamp) @@ -38,6 +38,25 @@ object MessageContentFuzzer { .build() } + /** + * Create metadata to match an [Envelope]. + */ + fun envelopeMetadata(source: RecipientId, destination: RecipientId): EnvelopeMetadata { + return EnvelopeMetadata( + sourceServiceId = Recipient.resolved(source).requireServiceId(), + sourceE164 = null, + sourceDeviceId = 1, + sealedSender = true, + groupId = null, + destinationServiceId = Recipient.resolved(destination).requireServiceId() + ) + } + + /** + * Create a random text message that will contain a body but may also contain + * - An expire timer value + * - Bold style body ranges + */ fun fuzzTextMessage(): Content { return Content.newBuilder() .setDataMessage( @@ -62,6 +81,13 @@ object MessageContentFuzzer { .build() } + /** + * Create a random media message that may be: + * - A text body + * - A text body with a quote that references an existing message + * - A text body with a quote that references a non existing message + * - A message with 0-2 attachment pointers and may contain a text body + */ fun fuzzMediaMessageWithBody(quoteAble: List = emptyList()): Content { return Content.newBuilder() .setDataMessage( @@ -105,6 +131,10 @@ object MessageContentFuzzer { .build() } + /** + * Creates a random media message that contains no traditional media content. It may be: + * - A reaction to a prior message + */ fun fuzzMediaMessageNoContent(previousMessages: List = emptyList()): Content { return Content.newBuilder() .setDataMessage( @@ -125,6 +155,10 @@ object MessageContentFuzzer { ).build() } + /** + * Create a random media message that can never contain a text body. It may be: + * - A sticker + */ fun fuzzMediaMessageNoText(previousMessages: List = emptyList()): Content { return Content.newBuilder() .setDataMessage( @@ -144,6 +178,9 @@ object MessageContentFuzzer { ).build() } + /** + * Generate a random [String]. + */ fun string(length: Int = 10, allowNullString: Boolean = false): String { var string = "" @@ -157,12 +194,18 @@ object MessageContentFuzzer { return string } + /** + * Generate a random [ByteString]. + */ fun byteString(length: Int = 512): ByteString { - return random.nextBytes(512).toProtoByteString() + return random.nextBytes(length).toProtoByteString() } - fun attachmentPointer(): SignalServiceProtos.AttachmentPointer { - return SignalServiceProtos.AttachmentPointer.newBuilder().run { + /** + * Generate a random [AttachmentPointer]. + */ + fun attachmentPointer(): AttachmentPointer { + return AttachmentPointer.newBuilder().run { cdnKey = string() contentType = mediaTypes.random(random) key = byteString() @@ -182,39 +225,10 @@ object MessageContentFuzzer { } } + /** + * Creates a server delivered timestamp that is always later than the envelope and server "received" timestamp. + */ fun fuzzServerDeliveredTimestamp(envelopeTimestamp: Long): Long { return envelopeTimestamp + 10 } - - fun fuzzMetadata(source: RecipientId, destination: RecipientId): EnvelopeMetadata { - return EnvelopeMetadata( - sourceServiceId = Recipient.resolved(source).requireServiceId(), - sourceE164 = null, - sourceDeviceId = 1, - sealedSender = true, - groupId = null, - destinationServiceId = Recipient.resolved(destination).requireServiceId() - ) - } - - fun fuzzProto(protoClazz: KClass) { - val newBuilder: Any = protoClazz.declaredFunctions.first { it.name == "newBuilder" }.call()!! - - val setters: List> = newBuilder::class.functions.filter { it.name.startsWith("set") && !it.name.contains("Bytes") } - - for (setter in setters) { - val type = setter.parameters[1].type.jvmErasure - when { - type == String::class -> setter.call(newBuilder, string()) - type == Int::class -> setter.call(newBuilder, random.nextInt()) - type == Long::class -> setter.call(newBuilder, random.nextLong()) - type == AttachmentPointer::class -> setter.call(newBuilder, attachmentPointer()) - type == Boolean::class -> setter.call(newBuilder, random.nextBoolean()) -// type.superclasses.contains(EnumLite::class) -> - else -> Log.e("CODY", "WHAT!?!?!?! ${setter.parameters[1].type.jvmErasure}") - } - } - - Log.e("CODY", newBuilder::class.functions.first { it.name == "build" }.call(newBuilder).toString()) - } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/DataMessageProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/DataMessageProcessor.kt index 95fc4834bc..8bac0e4b69 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/DataMessageProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/DataMessageProcessor.kt @@ -819,7 +819,7 @@ object DataMessageProcessor { val mentions: List = getMentions(message.bodyRangesList) val sticker: Attachment? = getStickerAttachment(envelope.timestamp, message) val attachments: List = message.attachmentsList.toPointers() - val messageRanges: BodyRangeList? = message.bodyRangesList.filter { it.hasStyle() }.toList().toBodyRangeList() + val messageRanges: BodyRangeList? = if (message.bodyRangesCount > 0) message.bodyRangesList.filter { it.hasStyle() }.toList().toBodyRangeList() else null handlePossibleExpirationUpdate(envelope, metadata, senderRecipient.id, threadRecipientId, groupId, message.expireTimer.seconds, receivedTime) diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java index 5643aa554e..a5cb62f34e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java @@ -201,7 +201,7 @@ import java.util.concurrent.TimeUnit; @SuppressWarnings({ "OptionalGetWithoutIsPresent", "OptionalIsPresent" }) public class MessageContentProcessor { - private static final String TAG = Log.tag(MessageContentProcessor.class); + public static final String TAG = Log.tag(MessageContentProcessor.class); private final Context context;