Fix some quoted messages having 'null' message bodies (#7532)
* Fix some quoted messages having 'null' message bodies
This commit is contained in:
		
							parent
							
								
									0d3c779455
								
							
						
					
					
						commit
						a476544761
					
				
							
								
								
									
										1
									
								
								changelog.d/7530.sdk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								changelog.d/7530.sdk
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | Fix a bug that caused messages with no formatted text to be quoted as "null". | ||||||
| @ -804,20 +804,12 @@ internal class LocalEchoEventFactory @Inject constructor( | |||||||
|             additionalContent: Content? = null, |             additionalContent: Content? = null, | ||||||
|     ): Event { |     ): Event { | ||||||
|         val messageContent = quotedEvent.getLastMessageContent() |         val messageContent = quotedEvent.getLastMessageContent() | ||||||
|         val textMsg = if (messageContent is MessageContentWithFormattedBody) { |         val formattedQuotedText = (messageContent as? MessageContentWithFormattedBody)?.formattedBody | ||||||
|             messageContent.formattedBody |         val textContent = createQuoteTextContent(messageContent?.body, formattedQuotedText, text, formattedText, autoMarkdown) | ||||||
|         } else { |  | ||||||
|             messageContent?.body |  | ||||||
|         } |  | ||||||
|         val quoteText = legacyRiotQuoteText(textMsg, text) |  | ||||||
|         val quoteFormattedText = "<blockquote>$textMsg</blockquote>$formattedText" |  | ||||||
| 
 |  | ||||||
|         return if (rootThreadEventId != null) { |         return if (rootThreadEventId != null) { | ||||||
|             createMessageEvent( |             createMessageEvent( | ||||||
|                     roomId, |                     roomId, | ||||||
|                     markdownParser |                     textContent.toThreadTextContent( | ||||||
|                             .parse(quoteText, force = true, advanced = autoMarkdown).copy(formattedText = quoteFormattedText) |  | ||||||
|                             .toThreadTextContent( |  | ||||||
|                                     rootThreadEventId = rootThreadEventId, |                                     rootThreadEventId = rootThreadEventId, | ||||||
|                                     latestThreadEventId = localEchoRepository.getLatestThreadEvent(rootThreadEventId), |                                     latestThreadEventId = localEchoRepository.getLatestThreadEvent(rootThreadEventId), | ||||||
|                                     msgType = MessageType.MSGTYPE_TEXT |                                     msgType = MessageType.MSGTYPE_TEXT | ||||||
| @ -827,31 +819,54 @@ internal class LocalEchoEventFactory @Inject constructor( | |||||||
|         } else { |         } else { | ||||||
|             createFormattedTextEvent( |             createFormattedTextEvent( | ||||||
|                     roomId, |                     roomId, | ||||||
|                     markdownParser.parse(quoteText, force = true, advanced = autoMarkdown).copy(formattedText = quoteFormattedText), |                     textContent, | ||||||
|                     MessageType.MSGTYPE_TEXT, |                     MessageType.MSGTYPE_TEXT, | ||||||
|                     additionalContent, |                     additionalContent, | ||||||
|             ) |             ) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun legacyRiotQuoteText(quotedText: String?, myText: String): String { |     private fun createQuoteTextContent( | ||||||
|         val messageParagraphs = quotedText?.split("\n\n".toRegex())?.dropLastWhile { it.isEmpty() }?.toTypedArray() |             quotedText: String?, | ||||||
|         return buildString { |             formattedQuotedText: String?, | ||||||
|             if (messageParagraphs != null) { |             text: String, | ||||||
|                 for (i in messageParagraphs.indices) { |             formattedText: String?, | ||||||
|                     if (messageParagraphs[i].isNotBlank()) { |             autoMarkdown: Boolean | ||||||
|  |     ): TextContent { | ||||||
|  |         val currentFormattedText = formattedText ?: if (autoMarkdown) { | ||||||
|  |             val parsed = markdownParser.parse(text, force = true, advanced = true) | ||||||
|  |             // If formattedText == text, formattedText is returned as null | ||||||
|  |             parsed.formattedText ?: parsed.text | ||||||
|  |         } else { | ||||||
|  |             text | ||||||
|  |         } | ||||||
|  |         val processedFormattedQuotedText = formattedQuotedText ?: quotedText | ||||||
|  | 
 | ||||||
|  |         val plainTextBody = buildString { | ||||||
|  |             val plainMessageParagraphs = quotedText?.split("\n\n".toRegex())?.dropLastWhile { it.isEmpty() }?.toTypedArray().orEmpty() | ||||||
|  |             plainMessageParagraphs.forEachIndexed { index, paragraph -> | ||||||
|  |                 if (paragraph.isNotBlank()) { | ||||||
|                     append("> ") |                     append("> ") | ||||||
|                         append(messageParagraphs[i]) |                     append(paragraph) | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                     if (i != messageParagraphs.lastIndex) { |                 if (index != plainMessageParagraphs.lastIndex) { | ||||||
|                     append("\n\n") |                     append("\n\n") | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             } |  | ||||||
|             append("\n\n") |             append("\n\n") | ||||||
|             append(myText) |             append(text) | ||||||
|         } |         } | ||||||
|  |         val formattedTextBody = buildString { | ||||||
|  |             if (!processedFormattedQuotedText.isNullOrBlank()) { | ||||||
|  |                 append("<blockquote>") | ||||||
|  |                 append(processedFormattedQuotedText) | ||||||
|  |                 append("</blockquote>") | ||||||
|  |             } | ||||||
|  |             append("<br/>") | ||||||
|  |             append(currentFormattedText) | ||||||
|  |         } | ||||||
|  |         return TextContent(plainTextBody, formattedTextBody) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     companion object { |     companion object { | ||||||
|  | |||||||
| @ -0,0 +1,241 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022 The Matrix.org Foundation C.I.C. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package org.matrix.android.sdk.internal.session.room.send | ||||||
|  | 
 | ||||||
|  | import org.amshove.kluent.internal.assertEquals | ||||||
|  | import org.junit.Before | ||||||
|  | import org.junit.Test | ||||||
|  | import org.matrix.android.sdk.api.session.events.model.Event | ||||||
|  | import org.matrix.android.sdk.api.session.events.model.EventType | ||||||
|  | import org.matrix.android.sdk.api.session.events.model.toContent | ||||||
|  | import org.matrix.android.sdk.api.session.events.model.toModel | ||||||
|  | import org.matrix.android.sdk.api.session.room.model.EditAggregatedSummary | ||||||
|  | import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary | ||||||
|  | import org.matrix.android.sdk.api.session.room.model.message.MessageContent | ||||||
|  | import org.matrix.android.sdk.api.session.room.model.message.MessageContentWithFormattedBody | ||||||
|  | import org.matrix.android.sdk.api.session.room.sender.SenderInfo | ||||||
|  | import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent | ||||||
|  | import org.matrix.android.sdk.api.util.TextContent | ||||||
|  | import org.matrix.android.sdk.test.fakes.FakeClock | ||||||
|  | import org.matrix.android.sdk.test.fakes.FakeContext | ||||||
|  | import org.matrix.android.sdk.test.fakes.internal.session.content.FakeThumbnailExtractor | ||||||
|  | import org.matrix.android.sdk.test.fakes.internal.session.permalinks.FakePermalinkFactory | ||||||
|  | import org.matrix.android.sdk.test.fakes.internal.session.room.send.FakeLocalEchoRepository | ||||||
|  | import org.matrix.android.sdk.test.fakes.internal.session.room.send.FakeMarkdownParser | ||||||
|  | import org.matrix.android.sdk.test.fakes.internal.session.room.send.FakeWaveFormSanitizer | ||||||
|  | import org.matrix.android.sdk.test.fakes.internal.session.room.send.pills.FakeTextPillsUtils | ||||||
|  | 
 | ||||||
|  | @Suppress("MaxLineLength") | ||||||
|  | class LocalEchoEventFactoryTests { | ||||||
|  | 
 | ||||||
|  |     companion object { | ||||||
|  |         internal const val A_USER_ID_1 = "@user_1:matrix.org" | ||||||
|  |         internal const val A_ROOM_ID = "!sUeOGZKsBValPTUMax:matrix.org" | ||||||
|  |         internal const val AN_EVENT_ID = "\$vApgexcL8Vfh-WxYKsFKCDooo67ttbjm3TiVKXaWijU" | ||||||
|  |         internal const val AN_EPOCH = 1655210176L | ||||||
|  | 
 | ||||||
|  |         val A_START_EVENT = Event( | ||||||
|  |                 type = EventType.STATE_ROOM_CREATE, | ||||||
|  |                 eventId = AN_EVENT_ID, | ||||||
|  |                 originServerTs = 1652435922563, | ||||||
|  |                 senderId = A_USER_ID_1, | ||||||
|  |                 roomId = A_ROOM_ID | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private val fakeContext = FakeContext() | ||||||
|  |     private val fakeMarkdownParser = FakeMarkdownParser() | ||||||
|  |     private val fakeTextPillsUtils = FakeTextPillsUtils() | ||||||
|  |     private val fakeThumbnailExtractor = FakeThumbnailExtractor() | ||||||
|  |     private val fakeWaveFormSanitizer = FakeWaveFormSanitizer() | ||||||
|  |     private val fakeLocalEchoRepository = FakeLocalEchoRepository() | ||||||
|  |     private val fakePermalinkFactory = FakePermalinkFactory() | ||||||
|  |     private val fakeClock = FakeClock() | ||||||
|  | 
 | ||||||
|  |     private val localEchoEventFactory = LocalEchoEventFactory( | ||||||
|  |             context = fakeContext.instance, | ||||||
|  |             userId = A_USER_ID_1, | ||||||
|  |             markdownParser = fakeMarkdownParser.instance, | ||||||
|  |             textPillsUtils = fakeTextPillsUtils.instance, | ||||||
|  |             thumbnailExtractor = fakeThumbnailExtractor.instance, | ||||||
|  |             waveformSanitizer = fakeWaveFormSanitizer.instance, | ||||||
|  |             localEchoRepository = fakeLocalEchoRepository.instance, | ||||||
|  |             permalinkFactory = fakePermalinkFactory.instance, | ||||||
|  |             clock = fakeClock | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     @Before | ||||||
|  |     fun setup() { | ||||||
|  |         fakeClock.givenEpoch(AN_EPOCH) | ||||||
|  |         fakeMarkdownParser.givenBoldMarkdown() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     fun `given a null quotedText, when a quote event is created, then the result message should only contain the new text after new lines`() { | ||||||
|  |         val event = createTimelineEvent(null, null) | ||||||
|  |         val quotedContent = localEchoEventFactory.createQuotedTextEvent( | ||||||
|  |                 roomId = A_ROOM_ID, | ||||||
|  |                 quotedEvent = event, | ||||||
|  |                 text = "Text", | ||||||
|  |                 formattedText = null, | ||||||
|  |                 autoMarkdown = false, | ||||||
|  |                 rootThreadEventId = null, | ||||||
|  |                 additionalContent = null, | ||||||
|  |         ).content.toModel<MessageContent>() | ||||||
|  |         assertEquals("\n\nText", quotedContent?.body) | ||||||
|  |         assertEquals("<br/>Text", (quotedContent as? MessageContentWithFormattedBody)?.formattedBody) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     fun `given a plain text quoted message, when a quote event is created, then the result message should contain both the quoted and new text`() { | ||||||
|  |         val event = createTimelineEvent("Quoted", null) | ||||||
|  |         val quotedContent = localEchoEventFactory.createQuotedTextEvent( | ||||||
|  |                 roomId = A_ROOM_ID, | ||||||
|  |                 quotedEvent = event, | ||||||
|  |                 text = "Text", | ||||||
|  |                 formattedText = null, | ||||||
|  |                 autoMarkdown = false, | ||||||
|  |                 rootThreadEventId = null, | ||||||
|  |                 additionalContent = null, | ||||||
|  |         ).content.toModel<MessageContent>() | ||||||
|  |         assertEquals("> Quoted\n\nText", quotedContent?.body) | ||||||
|  |         assertEquals("<blockquote>Quoted</blockquote><br/>Text", (quotedContent as? MessageContentWithFormattedBody)?.formattedBody) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     fun `given a formatted text quoted message, when a quote event is created, then the result message should contain both the formatted quote and new text`() { | ||||||
|  |         val event = createTimelineEvent("Quoted", "<b>Quoted</b>") | ||||||
|  |         val quotedContent = localEchoEventFactory.createQuotedTextEvent( | ||||||
|  |                 roomId = A_ROOM_ID, | ||||||
|  |                 quotedEvent = event, | ||||||
|  |                 text = "Text", | ||||||
|  |                 formattedText = null, | ||||||
|  |                 autoMarkdown = false, | ||||||
|  |                 rootThreadEventId = null, | ||||||
|  |                 additionalContent = null, | ||||||
|  |         ).content.toModel<MessageContent>() | ||||||
|  |         // This still uses the plain text version | ||||||
|  |         assertEquals("> Quoted\n\nText", quotedContent?.body) | ||||||
|  |         // This one has the formatted one | ||||||
|  |         assertEquals("<blockquote><b>Quoted</b></blockquote><br/>Text", (quotedContent as? MessageContentWithFormattedBody)?.formattedBody) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     fun `given formatted text quoted message and new message, when a quote event is created, then the result message should contain both the formatted quote and new formatted text`() { | ||||||
|  |         val event = createTimelineEvent("Quoted", "<b>Quoted</b>") | ||||||
|  |         val quotedContent = localEchoEventFactory.createQuotedTextEvent( | ||||||
|  |                 roomId = A_ROOM_ID, | ||||||
|  |                 quotedEvent = event, | ||||||
|  |                 text = "Text", | ||||||
|  |                 formattedText = "<b>Formatted text</b>", | ||||||
|  |                 autoMarkdown = false, | ||||||
|  |                 rootThreadEventId = null, | ||||||
|  |                 additionalContent = null, | ||||||
|  |         ).content.toModel<MessageContent>() | ||||||
|  |         // This still uses the plain text version | ||||||
|  |         assertEquals("> Quoted\n\nText", quotedContent?.body) | ||||||
|  |         // This one has the formatted one | ||||||
|  |         assertEquals( | ||||||
|  |                 "<blockquote><b>Quoted</b></blockquote><br/><b>Formatted text</b>", | ||||||
|  |                 (quotedContent as? MessageContentWithFormattedBody)?.formattedBody | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     fun `given formatted text quoted message and new message with autoMarkdown, when a quote event is created, then the result message should contain both the formatted quote and new formatted text, not the markdown processed text`() { | ||||||
|  |         val event = createTimelineEvent("Quoted", "<b>Quoted</b>") | ||||||
|  |         val quotedContent = localEchoEventFactory.createQuotedTextEvent( | ||||||
|  |                 roomId = A_ROOM_ID, | ||||||
|  |                 quotedEvent = event, | ||||||
|  |                 text = "Text", | ||||||
|  |                 formattedText = "<b>Formatted text</b>", | ||||||
|  |                 autoMarkdown = true, | ||||||
|  |                 rootThreadEventId = null, | ||||||
|  |                 additionalContent = null, | ||||||
|  |         ).content.toModel<MessageContent>() | ||||||
|  |         // This still uses the plain text version | ||||||
|  |         assertEquals("> Quoted\n\nText", quotedContent?.body) | ||||||
|  |         // This one has the formatted one | ||||||
|  |         assertEquals( | ||||||
|  |                 "<blockquote><b>Quoted</b></blockquote><br/><b>Formatted text</b>", | ||||||
|  |                 (quotedContent as? MessageContentWithFormattedBody)?.formattedBody | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     fun `given a formatted text quoted message and a new message with autoMarkdown, when a quote event is created, then the result message should contain both the formatted quote and new processed formatted text`() { | ||||||
|  |         val event = createTimelineEvent("Quoted", "<b>Quoted</b>") | ||||||
|  |         val quotedContent = localEchoEventFactory.createQuotedTextEvent( | ||||||
|  |                 roomId = A_ROOM_ID, | ||||||
|  |                 quotedEvent = event, | ||||||
|  |                 text = "**Text**", | ||||||
|  |                 formattedText = null, | ||||||
|  |                 autoMarkdown = true, | ||||||
|  |                 rootThreadEventId = null, | ||||||
|  |                 additionalContent = null, | ||||||
|  |         ).content.toModel<MessageContent>() | ||||||
|  |         // This still uses the markdown text version | ||||||
|  |         assertEquals("> Quoted\n\n**Text**", quotedContent?.body) | ||||||
|  |         // This one has the formatted one | ||||||
|  |         assertEquals( | ||||||
|  |                 "<blockquote><b>Quoted</b></blockquote><br/><b>Text</b>", | ||||||
|  |                 (quotedContent as? MessageContentWithFormattedBody)?.formattedBody | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     fun `given a plain text quoted message and a new message with autoMarkdown, when a quote event is created, then the result message should the plain text quote and new processed formatted text`() { | ||||||
|  |         val event = createTimelineEvent("Quoted", null) | ||||||
|  |         val quotedContent = localEchoEventFactory.createQuotedTextEvent( | ||||||
|  |                 roomId = A_ROOM_ID, | ||||||
|  |                 quotedEvent = event, | ||||||
|  |                 text = "**Text**", | ||||||
|  |                 formattedText = null, | ||||||
|  |                 autoMarkdown = true, | ||||||
|  |                 rootThreadEventId = null, | ||||||
|  |                 additionalContent = null, | ||||||
|  |         ).content.toModel<MessageContent>() | ||||||
|  |         // This still uses the markdown text version | ||||||
|  |         assertEquals("> Quoted\n\n**Text**", quotedContent?.body) | ||||||
|  |         // This one has the formatted one | ||||||
|  |         assertEquals( | ||||||
|  |                 "<blockquote>Quoted</blockquote><br/><b>Text</b>", | ||||||
|  |                 (quotedContent as? MessageContentWithFormattedBody)?.formattedBody | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private fun createTimelineEvent(quotedText: String?, formattedQuotedText: String?): TimelineEvent { | ||||||
|  |         val textContent = quotedText?.let { | ||||||
|  |             TextContent( | ||||||
|  |                     quotedText, | ||||||
|  |                     formattedQuotedText | ||||||
|  |             ).toMessageTextContent().toContent() | ||||||
|  |         } | ||||||
|  |         return TimelineEvent( | ||||||
|  |                 root = A_START_EVENT, | ||||||
|  |                 localId = 1234, | ||||||
|  |                 eventId = AN_EVENT_ID, | ||||||
|  |                 displayIndex = 0, | ||||||
|  |                 senderInfo = SenderInfo(A_USER_ID_1, A_USER_ID_1, true, null), | ||||||
|  |                 annotations = if (textContent != null) { | ||||||
|  |                     EventAnnotationsSummary( | ||||||
|  |                             editSummary = EditAggregatedSummary(latestContent = textContent, emptyList(), emptyList()) | ||||||
|  |                     ) | ||||||
|  |                 } else null | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,37 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022 The Matrix.org Foundation C.I.C. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package org.matrix.android.sdk.test.fakes | ||||||
|  | 
 | ||||||
|  | import android.content.ClipData | ||||||
|  | import android.content.ClipboardManager | ||||||
|  | import io.mockk.every | ||||||
|  | import io.mockk.just | ||||||
|  | import io.mockk.mockk | ||||||
|  | import io.mockk.runs | ||||||
|  | import io.mockk.verify | ||||||
|  | 
 | ||||||
|  | class FakeClipboardManager { | ||||||
|  |     val instance = mockk<ClipboardManager>() | ||||||
|  | 
 | ||||||
|  |     fun givenSetPrimaryClip() { | ||||||
|  |         every { instance.setPrimaryClip(any()) } just runs | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fun verifySetPrimaryClip(clipData: ClipData) { | ||||||
|  |         verify { instance.setPrimaryClip(clipData) } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,44 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022 The Matrix.org Foundation C.I.C | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package org.matrix.android.sdk.test.fakes | ||||||
|  | 
 | ||||||
|  | import android.net.ConnectivityManager | ||||||
|  | import android.net.Network | ||||||
|  | import android.net.NetworkCapabilities | ||||||
|  | import io.mockk.every | ||||||
|  | import io.mockk.mockk | ||||||
|  | 
 | ||||||
|  | class FakeConnectivityManager { | ||||||
|  |     val instance = mockk<ConnectivityManager>() | ||||||
|  | 
 | ||||||
|  |     fun givenNoActiveConnection() { | ||||||
|  |         every { instance.activeNetwork } returns null | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fun givenHasActiveConnection() { | ||||||
|  |         val network = mockk<Network>() | ||||||
|  |         every { instance.activeNetwork } returns network | ||||||
|  | 
 | ||||||
|  |         val networkCapabilities = FakeNetworkCapabilities() | ||||||
|  |         networkCapabilities.givenTransports( | ||||||
|  |                 NetworkCapabilities.TRANSPORT_CELLULAR, | ||||||
|  |                 NetworkCapabilities.TRANSPORT_WIFI, | ||||||
|  |                 NetworkCapabilities.TRANSPORT_VPN | ||||||
|  |         ) | ||||||
|  |         every { instance.getNetworkCapabilities(network) } returns networkCapabilities.instance | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,84 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022 The Matrix.org Foundation C.I.C. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package org.matrix.android.sdk.test.fakes | ||||||
|  | 
 | ||||||
|  | import android.content.ClipboardManager | ||||||
|  | import android.content.ContentResolver | ||||||
|  | import android.content.Context | ||||||
|  | import android.content.Intent | ||||||
|  | import android.net.ConnectivityManager | ||||||
|  | import android.net.Uri | ||||||
|  | import android.os.ParcelFileDescriptor | ||||||
|  | import io.mockk.every | ||||||
|  | import io.mockk.just | ||||||
|  | import io.mockk.mockk | ||||||
|  | import io.mockk.runs | ||||||
|  | import java.io.OutputStream | ||||||
|  | 
 | ||||||
|  | class FakeContext( | ||||||
|  |         private val contentResolver: ContentResolver = mockk() | ||||||
|  | ) { | ||||||
|  | 
 | ||||||
|  |     val instance = mockk<Context>() | ||||||
|  | 
 | ||||||
|  |     init { | ||||||
|  |         every { instance.contentResolver } returns contentResolver | ||||||
|  |         every { instance.applicationContext } returns instance | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fun givenFileDescriptor(uri: Uri, mode: String, factory: () -> ParcelFileDescriptor?) { | ||||||
|  |         val fileDescriptor = factory() | ||||||
|  |         every { contentResolver.openFileDescriptor(uri, mode, null) } returns fileDescriptor | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fun givenSafeOutputStreamFor(uri: Uri): OutputStream { | ||||||
|  |         val outputStream = mockk<OutputStream>(relaxed = true) | ||||||
|  |         every { contentResolver.openOutputStream(uri, "wt") } returns outputStream | ||||||
|  |         return outputStream | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fun givenMissingSafeOutputStreamFor(uri: Uri) { | ||||||
|  |         every { contentResolver.openOutputStream(uri, "wt") } returns null | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fun givenNoConnection() { | ||||||
|  |         val connectivityManager = FakeConnectivityManager() | ||||||
|  |         connectivityManager.givenNoActiveConnection() | ||||||
|  |         givenService(Context.CONNECTIVITY_SERVICE, ConnectivityManager::class.java, connectivityManager.instance) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fun <T> givenService(name: String, klass: Class<T>, service: T) { | ||||||
|  |         every { instance.getSystemService(name) } returns service | ||||||
|  |         every { instance.getSystemService(klass) } returns service | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fun givenHasConnection() { | ||||||
|  |         val connectivityManager = FakeConnectivityManager() | ||||||
|  |         connectivityManager.givenHasActiveConnection() | ||||||
|  |         givenService(Context.CONNECTIVITY_SERVICE, ConnectivityManager::class.java, connectivityManager.instance) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fun givenStartActivity(intent: Intent) { | ||||||
|  |         every { instance.startActivity(intent) } just runs | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fun givenClipboardManager(): FakeClipboardManager { | ||||||
|  |         val fakeClipboardManager = FakeClipboardManager() | ||||||
|  |         givenService(Context.CLIPBOARD_SERVICE, ClipboardManager::class.java, fakeClipboardManager.instance) | ||||||
|  |         return fakeClipboardManager | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,32 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022 The Matrix.org Foundation C.I.C. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package org.matrix.android.sdk.test.fakes | ||||||
|  | 
 | ||||||
|  | import android.net.NetworkCapabilities | ||||||
|  | import io.mockk.every | ||||||
|  | import io.mockk.mockk | ||||||
|  | 
 | ||||||
|  | class FakeNetworkCapabilities { | ||||||
|  |     val instance = mockk<NetworkCapabilities>() | ||||||
|  | 
 | ||||||
|  |     fun givenTransports(vararg type: Int) { | ||||||
|  |         every { instance.hasTransport(any()) } answers { | ||||||
|  |             val input = it.invocation.args.first() as Int | ||||||
|  |             type.contains(input) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,24 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022 The Matrix.org Foundation C.I.C. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package org.matrix.android.sdk.test.fakes.internal.session.content | ||||||
|  | 
 | ||||||
|  | import io.mockk.mockk | ||||||
|  | import org.matrix.android.sdk.internal.session.content.ThumbnailExtractor | ||||||
|  | 
 | ||||||
|  | class FakeThumbnailExtractor { | ||||||
|  |     internal val instance = mockk<ThumbnailExtractor>() | ||||||
|  | } | ||||||
| @ -0,0 +1,24 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022 The Matrix.org Foundation C.I.C. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package org.matrix.android.sdk.test.fakes.internal.session.permalinks | ||||||
|  | 
 | ||||||
|  | import io.mockk.mockk | ||||||
|  | import org.matrix.android.sdk.internal.session.permalinks.PermalinkFactory | ||||||
|  | 
 | ||||||
|  | class FakePermalinkFactory { | ||||||
|  |     internal val instance = mockk<PermalinkFactory>() | ||||||
|  | } | ||||||
| @ -0,0 +1,24 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022 The Matrix.org Foundation C.I.C. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package org.matrix.android.sdk.test.fakes.internal.session.room.send | ||||||
|  | 
 | ||||||
|  | import io.mockk.mockk | ||||||
|  | import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository | ||||||
|  | 
 | ||||||
|  | class FakeLocalEchoRepository { | ||||||
|  |     internal val instance = mockk<LocalEchoRepository>() | ||||||
|  | } | ||||||
| @ -0,0 +1,32 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022 The Matrix.org Foundation C.I.C. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package org.matrix.android.sdk.test.fakes.internal.session.room.send | ||||||
|  | 
 | ||||||
|  | import io.mockk.every | ||||||
|  | import io.mockk.mockk | ||||||
|  | import org.matrix.android.sdk.api.util.TextContent | ||||||
|  | import org.matrix.android.sdk.internal.session.room.send.MarkdownParser | ||||||
|  | 
 | ||||||
|  | class FakeMarkdownParser { | ||||||
|  |     internal val instance = mockk<MarkdownParser>() | ||||||
|  |     fun givenBoldMarkdown() { | ||||||
|  |         every { instance.parse(any(), any(), any()) } answers { | ||||||
|  |             val text = arg<String>(0) | ||||||
|  |             TextContent(text, "<b>${text.replace("*", "")}</b>") | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,24 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022 The Matrix.org Foundation C.I.C. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package org.matrix.android.sdk.test.fakes.internal.session.room.send | ||||||
|  | 
 | ||||||
|  | import io.mockk.mockk | ||||||
|  | import org.matrix.android.sdk.internal.session.room.send.WaveFormSanitizer | ||||||
|  | 
 | ||||||
|  | class FakeWaveFormSanitizer { | ||||||
|  |     internal val instance = mockk<WaveFormSanitizer>() | ||||||
|  | } | ||||||
| @ -0,0 +1,24 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2022 The Matrix.org Foundation C.I.C. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package org.matrix.android.sdk.test.fakes.internal.session.room.send.pills | ||||||
|  | 
 | ||||||
|  | import io.mockk.mockk | ||||||
|  | import org.matrix.android.sdk.internal.session.room.send.pills.TextPillsUtils | ||||||
|  | 
 | ||||||
|  | class FakeTextPillsUtils { | ||||||
|  |     internal val instance = mockk<TextPillsUtils>() | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user