From 628d02beb1e95832ca8ae6058ff13ec28ed3a532 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 24 Nov 2023 16:10:41 +0100 Subject: [PATCH] Add test for DefaultBugReporter --- features/rageshake/impl/build.gradle.kts | 9 ++ .../impl/reporter/BugReporterUrlProvider.kt | 23 +++ .../impl/reporter/DefaultBugReporter.kt | 3 +- .../reporter/DefaultBugReporterUrlProvider.kt | 34 ++++ .../impl/reporter/DefaultBugReporterTest.kt | 150 ++++++++++++++++++ .../DefaultBugReporterUrlProviderTest.kt | 31 ++++ gradle/libs.versions.toml | 1 + .../test/strings/FakeStringProvider.kt | 35 ++++ 8 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/BugReporterUrlProvider.kt create mode 100644 features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProvider.kt create mode 100755 features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt create mode 100644 features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProviderTest.kt create mode 100644 services/toolbox/test/src/main/kotlin/io/element/android/services/toolbox/test/strings/FakeStringProvider.kt diff --git a/features/rageshake/impl/build.gradle.kts b/features/rageshake/impl/build.gradle.kts index 99f57a1936..f87858ddc2 100644 --- a/features/rageshake/impl/build.gradle.kts +++ b/features/rageshake/impl/build.gradle.kts @@ -23,6 +23,12 @@ plugins { android { namespace = "io.element.android.features.rageshake.impl" + + testOptions { + unitTests { + isIncludeAndroidResources = true + } + } } anvil { @@ -57,6 +63,9 @@ dependencies { testImplementation(libs.test.turbine) testImplementation(libs.test.mockk) testImplementation(projects.libraries.matrix.test) + testImplementation(projects.libraries.sessionStorage.implMemory) testImplementation(projects.features.rageshake.test) testImplementation(projects.tests.testutils) + testImplementation(projects.services.toolbox.test) + testImplementation(libs.network.mockwebserver) } diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/BugReporterUrlProvider.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/BugReporterUrlProvider.kt new file mode 100644 index 0000000000..c380ab157d --- /dev/null +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/BugReporterUrlProvider.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * 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 io.element.android.features.rageshake.impl.reporter + +import okhttp3.HttpUrl + +fun interface BugReporterUrlProvider { + fun provide(): HttpUrl +} diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt index 5d7a0160fc..3bd7405f52 100755 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt @@ -75,6 +75,7 @@ class DefaultBugReporter @Inject constructor( private val userAgentProvider: UserAgentProvider, private val sessionStore: SessionStore, private val buildMeta: BuildMeta, + private val bugReporterUrlProvider: BugReporterUrlProvider, ) : BugReporter { companion object { // filenames @@ -223,7 +224,7 @@ class DefaultBugReporter @Inject constructor( // build the request val request = Request.Builder() - .url(context.getString(R.string.bug_report_url)) + .url(bugReporterUrlProvider.provide()) .post(requestBody) .build() diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProvider.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProvider.kt new file mode 100644 index 0000000000..5907a9a1c9 --- /dev/null +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProvider.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * 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 io.element.android.features.rageshake.impl.reporter + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.rageshake.impl.R +import io.element.android.libraries.di.AppScope +import io.element.android.services.toolbox.api.strings.StringProvider +import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrl +import javax.inject.Inject + +@ContributesBinding(AppScope::class) +class DefaultBugReporterUrlProvider @Inject constructor( + private val stringProvider: StringProvider +) : BugReporterUrlProvider { + override fun provide(): HttpUrl { + return stringProvider.getString(R.string.bug_report_url).toHttpUrl() + } +} diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt new file mode 100755 index 0000000000..53039176a8 --- /dev/null +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterTest.kt @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * 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 io.element.android.features.rageshake.impl.reporter + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.rageshake.api.reporter.BugReporterListener +import io.element.android.features.rageshake.test.crash.FakeCrashDataStore +import io.element.android.features.rageshake.test.screenshot.FakeScreenshotHolder +import io.element.android.libraries.matrix.test.core.aBuildMeta +import io.element.android.libraries.network.useragent.DefaultUserAgentProvider +import io.element.android.libraries.sessionstorage.impl.memory.InMemorySessionStore +import io.element.android.services.toolbox.test.systemclock.FakeSystemClock +import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import okhttp3.OkHttpClient +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.MockWebServer +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment + +@RunWith(RobolectricTestRunner::class) +class DefaultBugReporterTest { + @Test + fun `test sendBugReport success`() = runTest { + val server = MockWebServer() + server.enqueue( + MockResponse() + .setResponseCode(200) + ) + server.start() + val sut = createDefaultBugReporter(server) + var onUploadCancelledCalled = false + var onUploadFailedCalled = false + var progressValues = mutableListOf() + var onUploadSucceedCalled = false + sut.sendBugReport( + withDevicesLogs = true, + withCrashLogs = true, + withScreenshot = true, + theBugDescription = "a bug occurred", + canContact = true, + listener = object : BugReporterListener { + override fun onUploadCancelled() { + onUploadCancelledCalled = true + } + + override fun onUploadFailed(reason: String?) { + onUploadFailedCalled = true + } + + override fun onProgress(progress: Int) { + progressValues.add(progress) + } + + override fun onUploadSucceed() { + onUploadSucceedCalled = true + } + }, + ) + server.shutdown() + assertThat(onUploadCancelledCalled).isFalse() + assertThat(onUploadFailedCalled).isFalse() + assertThat(progressValues.size).isEqualTo(10) + assertThat(onUploadSucceedCalled).isTrue() + } + + @Test + fun `test sendBugReport error`() = runTest { + val server = MockWebServer() + server.enqueue( + MockResponse() + .setResponseCode(400) + .setBody("""{"error": "An error body"}""") + ) + server.start() + val sut = createDefaultBugReporter(server) + var onUploadCancelledCalled = false + var onUploadFailedCalled = false + var onUploadFailedReason: String? = null + var progressValues = mutableListOf() + var onUploadSucceedCalled = false + sut.sendBugReport( + withDevicesLogs = true, + withCrashLogs = true, + withScreenshot = true, + theBugDescription = "a bug occurred", + canContact = true, + listener = object : BugReporterListener { + override fun onUploadCancelled() { + onUploadCancelledCalled = true + } + + override fun onUploadFailed(reason: String?) { + onUploadFailedCalled = true + onUploadFailedReason = reason + } + + override fun onProgress(progress: Int) { + progressValues.add(progress) + } + + override fun onUploadSucceed() { + onUploadSucceedCalled = true + } + }, + ) + server.shutdown() + assertThat(onUploadCancelledCalled).isFalse() + assertThat(onUploadFailedCalled).isTrue() + assertThat(onUploadFailedReason).isEqualTo("An error body") + assertThat(progressValues.size).isEqualTo(10) + assertThat(onUploadSucceedCalled).isFalse() + } + + private fun TestScope.createDefaultBugReporter( + server: MockWebServer + ): DefaultBugReporter { + val buildMeta = aBuildMeta() + return DefaultBugReporter( + context = RuntimeEnvironment.getApplication(), + screenshotHolder = FakeScreenshotHolder(), + crashDataStore = FakeCrashDataStore(), + coroutineScope = this, + systemClock = FakeSystemClock(), + coroutineDispatchers = testCoroutineDispatchers(), + okHttpClient = { OkHttpClient.Builder().build() }, + userAgentProvider = DefaultUserAgentProvider(buildMeta), + sessionStore = InMemorySessionStore(), + buildMeta = buildMeta, + bugReporterUrlProvider = { server.url("/") } + ) + } +} diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProviderTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProviderTest.kt new file mode 100644 index 0000000000..a5c36df185 --- /dev/null +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporterUrlProviderTest.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * 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 io.element.android.features.rageshake.impl.reporter + +import com.google.common.truth.Truth.assertThat +import io.element.android.services.toolbox.test.strings.FakeStringProvider +import okhttp3.HttpUrl.Companion.toHttpUrl +import org.junit.Test + +class DefaultBugReporterUrlProviderTest { + @Test + fun `test DefaultBugReporterUrlProvider`() { + val sut = DefaultBugReporterUrlProvider(FakeStringProvider("https://example.org")) + val result = sut.provide() + assertThat(result).isEqualTo("https://example.org".toHttpUrl()) + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a79d92b7e1..ae2a0f71a2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -113,6 +113,7 @@ network_okhttp_bom = "com.squareup.okhttp3:okhttp-bom:4.12.0" network_okhttp_logging = { module = "com.squareup.okhttp3:logging-interceptor" } network_okhttp_okhttp = { module = "com.squareup.okhttp3:okhttp" } network_okhttp = { module = "com.squareup.okhttp3:okhttp" } +network_mockwebserver = { module = "com.squareup.okhttp3:mockwebserver" } network_retrofit = "com.squareup.retrofit2:retrofit:2.9.0" network_retrofit_converter_serialization = "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0" diff --git a/services/toolbox/test/src/main/kotlin/io/element/android/services/toolbox/test/strings/FakeStringProvider.kt b/services/toolbox/test/src/main/kotlin/io/element/android/services/toolbox/test/strings/FakeStringProvider.kt new file mode 100644 index 0000000000..c9f9a84fc9 --- /dev/null +++ b/services/toolbox/test/src/main/kotlin/io/element/android/services/toolbox/test/strings/FakeStringProvider.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * 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 io.element.android.services.toolbox.test.strings + +import io.element.android.services.toolbox.api.strings.StringProvider + +class FakeStringProvider( + private val defaultResult: String = "A string" +) : StringProvider { + override fun getString(resId: Int): String { + return defaultResult + } + + override fun getString(resId: Int, vararg formatArgs: Any?): String { + return defaultResult + } + + override fun getQuantityString(resId: Int, quantity: Int, vararg formatArgs: Any?): String { + return defaultResult + } +}