diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8860623..00fe744 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -65,6 +65,8 @@ dependencies { val daggerVersion = "2.40.5" val jUnitVersion = "5.8.2" + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0") + implementation("androidx.core:core-ktx:1.7.0") implementation("androidx.appcompat:appcompat:1.4.1") implementation("androidx.activity:activity-ktx:1.4.0") @@ -98,7 +100,7 @@ dependencies { implementation("com.squareup.moshi:moshi-adapters:$moshiVersion") kapt("com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0") + implementation("at.connyduck:kotlin-result-calladapter:1.0.0") implementation("com.fxn769:pix:1.5.6") diff --git a/app/src/main/kotlin/at/connyduck/pixelcat/components/compose/SendStatusService.kt b/app/src/main/kotlin/at/connyduck/pixelcat/components/compose/SendStatusService.kt index 560ec17..65cd9f3 100644 --- a/app/src/main/kotlin/at/connyduck/pixelcat/components/compose/SendStatusService.kt +++ b/app/src/main/kotlin/at/connyduck/pixelcat/components/compose/SendStatusService.kt @@ -36,7 +36,6 @@ import at.connyduck.pixelcat.components.util.getMimeType import at.connyduck.pixelcat.db.AccountManager import at.connyduck.pixelcat.model.NewStatus import at.connyduck.pixelcat.network.FediverseApi -import at.connyduck.pixelcat.network.calladapter.NetworkResponseError import dagger.android.DaggerService import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -49,6 +48,7 @@ import okhttp3.MediaType.Companion.toMediaType import okhttp3.MultipartBody import okhttp3.RequestBody.Companion.asRequestBody import java.io.File +import java.io.IOException import java.util.Timer import java.util.TimerTask import java.util.UUID @@ -173,7 +173,7 @@ class SendStatusService : DaggerService(), CoroutineScope { account.domain, statusToSend.idempotencyKey, newStatus - ).fold( + ).fold( { statusesToSend.remove(id) stopSelfWhenDone() @@ -192,20 +192,8 @@ class SendStatusService : DaggerService(), CoroutineScope { val statusToSend = statusesToSend[id] ?: return when (error) { - is NetworkResponseError.ApiError, is UnrecoverableError -> { - // the server refused to accept the status, save toot & show error message - // TODO saveToDrafts - - val builder = NotificationCompat.Builder(this@SendStatusService, CHANNEL_ID) - .setSmallIcon(R.drawable.ic_cat) - .setContentTitle(getString(R.string.send_status_notification_error_title)) - // .setContentText(getString(R.string.send_toot_notification_saved_content)) - .setColor(getColorForAttr(android.R.attr.colorPrimary)) - - notificationManager.cancel(id) - notificationManager.notify(errorNotificationId--, builder.build()) - } - else -> { + is IOException -> { + // possibly a network problem, we might still have a chance sending the status var backoff = TimeUnit.SECONDS.toMillis(statusToSend.retries.toLong()) if (backoff > MAX_RETRY_INTERVAL) { backoff = MAX_RETRY_INTERVAL @@ -220,6 +208,19 @@ class SendStatusService : DaggerService(), CoroutineScope { backoff ) } + else -> { + // the server refused to accept the status, save toot & show error message + // TODO saveToDrafts + + val builder = NotificationCompat.Builder(this@SendStatusService, CHANNEL_ID) + .setSmallIcon(R.drawable.ic_cat) + .setContentTitle(getString(R.string.send_status_notification_error_title)) + // .setContentText(getString(R.string.send_toot_notification_saved_content)) + .setColor(getColorForAttr(android.R.attr.colorPrimary)) + + notificationManager.cancel(id) + notificationManager.notify(errorNotificationId--, builder.build()) + } } } diff --git a/app/src/main/kotlin/at/connyduck/pixelcat/components/timeline/TimelineUseCases.kt b/app/src/main/kotlin/at/connyduck/pixelcat/components/timeline/TimelineUseCases.kt index 1eae3a3..964a7bf 100644 --- a/app/src/main/kotlin/at/connyduck/pixelcat/components/timeline/TimelineUseCases.kt +++ b/app/src/main/kotlin/at/connyduck/pixelcat/components/timeline/TimelineUseCases.kt @@ -6,7 +6,6 @@ import at.connyduck.pixelcat.db.entitity.StatusEntity import at.connyduck.pixelcat.db.entitity.toEntity import at.connyduck.pixelcat.model.Status import at.connyduck.pixelcat.network.FediverseApi -import at.connyduck.pixelcat.network.calladapter.NetworkResponse import javax.inject.Inject import javax.inject.Singleton @@ -43,8 +42,8 @@ class TimelineUseCases @Inject constructor( ) } - private suspend fun NetworkResponse.updateStatusInDb() { - fold( + private suspend fun Result.updateStatusInDb() { + fold( { updatedStatus -> val accountId = accountManager.activeAccount()?.id!! val updatedStatusEntity = updatedStatus.toEntity(accountId) diff --git a/app/src/main/kotlin/at/connyduck/pixelcat/dagger/NetworkModule.kt b/app/src/main/kotlin/at/connyduck/pixelcat/dagger/NetworkModule.kt index 87300ad..eada8c3 100644 --- a/app/src/main/kotlin/at/connyduck/pixelcat/dagger/NetworkModule.kt +++ b/app/src/main/kotlin/at/connyduck/pixelcat/dagger/NetworkModule.kt @@ -19,6 +19,7 @@ package at.connyduck.pixelcat.dagger +import at.connyduck.calladapter.kotlinresult.KotlinResultCallAdapterFactory import at.connyduck.pixelcat.BuildConfig import at.connyduck.pixelcat.db.AccountManager import at.connyduck.pixelcat.model.Notification @@ -26,7 +27,6 @@ import at.connyduck.pixelcat.network.FediverseApi import at.connyduck.pixelcat.network.InstanceSwitchAuthInterceptor import at.connyduck.pixelcat.network.RefreshTokenAuthenticator import at.connyduck.pixelcat.network.UserAgentInterceptor -import at.connyduck.pixelcat.network.calladapter.NetworkResponseAdapterFactory import com.squareup.moshi.Moshi import com.squareup.moshi.adapters.EnumJsonAdapter import com.squareup.moshi.adapters.Rfc3339DateJsonAdapter @@ -82,7 +82,7 @@ class NetworkModule { return Retrofit.Builder() .baseUrl("https://" + FediverseApi.PLACEHOLDER_DOMAIN) .client(httpClient) - .addCallAdapterFactory(NetworkResponseAdapterFactory()) + .addCallAdapterFactory(KotlinResultCallAdapterFactory.create()) .addConverterFactory(MoshiConverterFactory.create(moshi)) .build() } diff --git a/app/src/main/kotlin/at/connyduck/pixelcat/network/FediverseApi.kt b/app/src/main/kotlin/at/connyduck/pixelcat/network/FediverseApi.kt index 5f63721..ed6fc18 100644 --- a/app/src/main/kotlin/at/connyduck/pixelcat/network/FediverseApi.kt +++ b/app/src/main/kotlin/at/connyduck/pixelcat/network/FediverseApi.kt @@ -28,7 +28,6 @@ import at.connyduck.pixelcat.model.Notification import at.connyduck.pixelcat.model.Relationship import at.connyduck.pixelcat.model.Status import at.connyduck.pixelcat.model.StatusContext -import at.connyduck.pixelcat.network.calladapter.NetworkResponse import okhttp3.MultipartBody import retrofit2.http.Body import retrofit2.http.Field @@ -57,7 +56,7 @@ interface FediverseApi { @Field("website") clientWebsite: String, @Field("redirect_uris") redirectUris: String, @Field("scopes") scopes: String - ): NetworkResponse + ): Result @FormUrlEncoded @POST("oauth/token") @@ -68,7 +67,7 @@ interface FediverseApi { @Field("redirect_uri") redirectUri: String, @Field("code") code: String, @Field("grant_type") grantType: String = "authorization_code" - ): NetworkResponse + ): Result @FormUrlEncoded @POST("oauth/token") @@ -78,17 +77,17 @@ interface FediverseApi { @Field("client_secret") clientSecret: String, @Field("refresh_token") refreshToken: String, @Field("grant_type") grantType: String = "refresh_token" - ): NetworkResponse + ): Result @GET("api/v1/accounts/verify_credentials") - suspend fun accountVerifyCredentials(): NetworkResponse + suspend fun accountVerifyCredentials(): Result @GET("api/v1/timelines/home") suspend fun homeTimeline( @Query("max_id") maxId: String? = null, @Query("since_id") sinceId: String? = null, @Query("limit") limit: Int? = null - ): NetworkResponse> + ): Result> @GET("api/v1/accounts/{id}/statuses") suspend fun accountTimeline( @@ -99,12 +98,12 @@ interface FediverseApi { @Query("exclude_replies") excludeReplies: Boolean? = false, @Query("only_media") onlyMedia: Boolean? = true, @Query("pinned") pinned: Boolean? = false - ): NetworkResponse + ): Result @GET("api/v1/accounts/{id}") suspend fun account( @Path("id") accountId: String - ): NetworkResponse + ): Result @GET("api/v1/accounts/{id}/statuses") suspend fun accountStatuses( @@ -114,50 +113,50 @@ interface FediverseApi { @Query("limit") limit: Int? = null, @Query("only_media") onlyMedia: Boolean? = null, @Query("exclude_reblogs") excludeReblogs: Boolean? = null - ): NetworkResponse> + ): Result> @FormUrlEncoded @POST("api/v1/accounts/{id}/follow") suspend fun followAccount( @Path("id") accountId: String, @Field("reblogs") showReblogs: Boolean - ): NetworkResponse + ): Result @POST("api/v1/accounts/{id}/unfollow") suspend fun unfollowAccount( @Path("id") accountId: String - ): NetworkResponse + ): Result @POST("api/v1/accounts/{id}/block") suspend fun blockAccount( @Path("id") accountId: String - ): NetworkResponse + ): Result @POST("api/v1/accounts/{id}/unblock") suspend fun unblockAccount( @Path("id") accountId: String - ): NetworkResponse + ): Result @POST("api/v1/accounts/{id}/mute") suspend fun muteAccount( @Path("id") accountId: String - ): NetworkResponse + ): Result @POST("api/v1/accounts/{id}/unmute") suspend fun unmuteAccount( @Path("id") accountId: String - ): NetworkResponse + ): Result @GET("api/v1/accounts/relationships") suspend fun relationships( @Query("id[]") accountIds: List - ): NetworkResponse> + ): Result> @Multipart @POST("api/v1/media") suspend fun uploadMedia( @Part file: MultipartBody.Part - ): NetworkResponse + ): Result @POST("api/v1/statuses") suspend fun createStatus( @@ -165,42 +164,42 @@ interface FediverseApi { @Header(DOMAIN_HEADER) domain: String, @Header("Idempotency-Key") idempotencyKey: String, @Body status: NewStatus - ): NetworkResponse + ): Result @POST("api/v1/statuses") suspend fun reply( @Body status: NewStatus - ): NetworkResponse + ): Result @POST("api/v1/statuses/{id}/favourite") suspend fun favouriteStatus( @Path("id") statusId: String - ): NetworkResponse + ): Result @POST("api/v1/statuses/{id}/unfavourite") suspend fun unfavouriteStatus( @Path("id") statusId: String - ): NetworkResponse + ): Result @POST("api/v1/statuses/{id}/reblog") suspend fun reblogStatus( @Path("id") statusId: String - ): NetworkResponse + ): Result @POST("api/v1/statuses/{id}/unreblog") suspend fun unreblogStatus( @Path("id") statusId: String - ): NetworkResponse + ): Result @GET("api/v1/statuses/{id}") suspend fun status( @Path("id") statusId: String - ): NetworkResponse + ): Result @GET("api/v1/statuses/{id}/context") suspend fun statusContext( @Path("id") statusId: String - ): NetworkResponse + ): Result @GET("api/v1/notifications") suspend fun notifications( @@ -208,5 +207,5 @@ interface FediverseApi { @Query("since_id") sinceId: String? = null, @Query("limit") limit: Int? = null, @Query("exclude_types[]") excludes: Set? = null - ): NetworkResponse> + ): Result> } diff --git a/app/src/main/kotlin/at/connyduck/pixelcat/network/calladapter/NetworkCallAdapter.kt b/app/src/main/kotlin/at/connyduck/pixelcat/network/calladapter/NetworkCallAdapter.kt deleted file mode 100644 index b0df5f5..0000000 --- a/app/src/main/kotlin/at/connyduck/pixelcat/network/calladapter/NetworkCallAdapter.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2020 Conny Duck - * - * This file is part of Pixelcat. - * - * Pixelcat is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelcat is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package at.connyduck.pixelcat.network.calladapter - -import retrofit2.Call -import retrofit2.CallAdapter -import java.lang.reflect.Type - -class NetworkCallAdapter( - private val successType: Type -) : CallAdapter>> { - - override fun responseType(): Type = successType - - override fun adapt(call: Call): Call> { - return NetworkResponseCall(call) - } -} diff --git a/app/src/main/kotlin/at/connyduck/pixelcat/network/calladapter/NetworkResponse.kt b/app/src/main/kotlin/at/connyduck/pixelcat/network/calladapter/NetworkResponse.kt deleted file mode 100644 index e5404a6..0000000 --- a/app/src/main/kotlin/at/connyduck/pixelcat/network/calladapter/NetworkResponse.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2020 Conny Duck - * - * This file is part of Pixelcat. - * - * Pixelcat is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelcat is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package at.connyduck.pixelcat.network.calladapter - -import java.io.IOException - -sealed class NetworkResponse { - - data class Success(val body: T) : NetworkResponse() - - data class Failure(val reason: NetworkResponseError) : NetworkResponse() - - inline fun fold(onSuccess: (A) -> B, onFailure: (NetworkResponseError) -> B): B = when (this) { - is Success -> onSuccess(body) - is Failure -> onFailure(reason) - } -} - -sealed class NetworkResponseError : Throwable() { - - data class ApiError(val code: Int) : NetworkResponseError() - - /** - * Network error - */ - data class NetworkError(val error: IOException) : NetworkResponseError() - - /** - * For example, json parsing error - */ - data class UnknownError(val error: Throwable?) : NetworkResponseError() -} diff --git a/app/src/main/kotlin/at/connyduck/pixelcat/network/calladapter/NetworkResponseAdapterFactory.kt b/app/src/main/kotlin/at/connyduck/pixelcat/network/calladapter/NetworkResponseAdapterFactory.kt deleted file mode 100644 index 9b7260f..0000000 --- a/app/src/main/kotlin/at/connyduck/pixelcat/network/calladapter/NetworkResponseAdapterFactory.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2020 Conny Duck - * - * This file is part of Pixelcat. - * - * Pixelcat is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelcat is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package at.connyduck.pixelcat.network.calladapter - -import retrofit2.Call -import retrofit2.CallAdapter -import retrofit2.Retrofit -import java.lang.reflect.ParameterizedType -import java.lang.reflect.Type - -class NetworkResponseAdapterFactory : CallAdapter.Factory() { - - override fun get( - returnType: Type, - annotations: Array, - retrofit: Retrofit - ): CallAdapter<*, *>? { - - // suspend functions wrap the response type in `Call` - if (Call::class.java != getRawType(returnType)) { - return null - } - - // check first that the return type is `ParameterizedType` - check(returnType is ParameterizedType) { - "return type must be parameterized as Call> or Call>" - } - - // get the response type inside the `Call` type - val responseType = getParameterUpperBound(0, returnType) - // if the response type is not ApiResponse then we can't handle this type, so we return null - if (getRawType(responseType) != NetworkResponse::class.java) { - return null - } - - // the response type is ApiResponse and should be parameterized - check(responseType is ParameterizedType) { "Response must be parameterized as NetworkResponse or NetworkResponse" } - - val successBodyType = getParameterUpperBound(0, responseType) - - return NetworkCallAdapter(successBodyType) - } -} diff --git a/app/src/main/kotlin/at/connyduck/pixelcat/network/calladapter/NetworkResponseCall.kt b/app/src/main/kotlin/at/connyduck/pixelcat/network/calladapter/NetworkResponseCall.kt deleted file mode 100644 index a436d08..0000000 --- a/app/src/main/kotlin/at/connyduck/pixelcat/network/calladapter/NetworkResponseCall.kt +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2020 Conny Duck - * - * This file is part of Pixelcat. - * - * Pixelcat is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelcat is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package at.connyduck.pixelcat.network.calladapter - -import okhttp3.Request -import okio.Timeout -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response -import java.io.IOException - -internal class NetworkResponseCall( - private val delegate: Call -) : Call> { - - override fun enqueue(callback: Callback>) { - return delegate.enqueue( - object : Callback { - override fun onResponse(call: Call, response: Response) { - val body = response.body() - - if (response.isSuccessful) { - if (body != null) { - callback.onResponse( - this@NetworkResponseCall, - Response.success(NetworkResponse.Success(body)) - ) - } else { - // Response is successful but the body is null - callback.onResponse( - this@NetworkResponseCall, - Response.success( - NetworkResponse.Failure( - NetworkResponseError.ApiError( - response.code() - ) - ) - ) - ) - } - } else { - callback.onResponse( - this@NetworkResponseCall, - Response.success( - NetworkResponse.Failure( - NetworkResponseError.ApiError( - response.code() - ) - ) - ) - ) - } - } - - override fun onFailure(call: Call, throwable: Throwable) { - val networkResponse = when (throwable) { - is IOException -> NetworkResponse.Failure( - NetworkResponseError.NetworkError( - throwable - ) - ) - else -> NetworkResponse.Failure(NetworkResponseError.UnknownError(throwable)) - } - callback.onResponse(this@NetworkResponseCall, Response.success(networkResponse)) - } - } - ) - } - - override fun isExecuted() = delegate.isExecuted - - override fun clone() = NetworkResponseCall(delegate.clone()) - - override fun isCanceled() = delegate.isCanceled - - override fun cancel() = delegate.cancel() - - override fun execute(): Response> { - throw UnsupportedOperationException("NetworkResponseCall doesn't support synchronized execution") - } - - override fun request(): Request = delegate.request() - - override fun timeout(): Timeout = delegate.timeout() -} diff --git a/app/src/test/kotlin/at/connyduck/pixelcat/network/calladapter/ApiTest.kt b/app/src/test/kotlin/at/connyduck/pixelcat/network/calladapter/ApiTest.kt deleted file mode 100644 index 61b73a2..0000000 --- a/app/src/test/kotlin/at/connyduck/pixelcat/network/calladapter/ApiTest.kt +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2020 Conny Duck - * - * This file is part of Pixelcat. - * - * Pixelcat is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelcat is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package at.connyduck.pixelcat.network.calladapter - -import com.squareup.moshi.Moshi -import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory -import kotlinx.coroutines.runBlocking -import okhttp3.OkHttpClient -import okhttp3.mockwebserver.MockResponse -import okhttp3.mockwebserver.MockWebServer -import okhttp3.mockwebserver.SocketPolicy -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import retrofit2.Retrofit -import retrofit2.converter.moshi.MoshiConverterFactory -import java.io.IOException - -class ApiTest { - - private var mockWebServer = MockWebServer() - - private lateinit var api: TestApi - - @BeforeEach - fun setup() { - mockWebServer.start() - - val moshi = Moshi.Builder() - .add(KotlinJsonAdapterFactory()) - .build() - - api = Retrofit.Builder() - .baseUrl(mockWebServer.url("/")) - .addCallAdapterFactory(NetworkResponseAdapterFactory()) - .addConverterFactory(MoshiConverterFactory.create(moshi)) - .client(OkHttpClient()) - .build() - .create(TestApi::class.java) - } - - @AfterEach - fun shutdown() { - mockWebServer.shutdown() - } - - private fun mockResponse(responseCode: Int, body: String = "") = MockResponse() - .setResponseCode(responseCode) - .setBody(body) - - @Test - fun `should return the correct test object`() { - val response = mockResponse( - 200, - """ - { - "lets": "not", - "test": 1 - } - """ - ) - - mockWebServer.enqueue(response) - - val responseObject = runBlocking { - api.testEndpoint() - } - - assertEquals( - NetworkResponse.Success(TestResponseClass("not", 1)), - responseObject - ) - } - - @Test - fun `should return a ApiError failure when the server returns error 500`() { - val errorCode = 500 - val response = mockResponse(errorCode) - - mockWebServer.enqueue(response) - - val responseObject = runBlocking { - api.testEndpoint() - } - - assertEquals( - NetworkResponse.Failure(NetworkResponseError.ApiError(errorCode)), - responseObject - ) - } - - @Test - fun `should return a NetworkError failure when the network fails`() { - mockWebServer.enqueue(MockResponse().apply { socketPolicy = SocketPolicy.DISCONNECT_AFTER_REQUEST }) - val responseObject = runBlocking { - api.testEndpoint() - } - - assertEquals( - NetworkResponse.Failure( - NetworkResponseError.NetworkError( - object : IOException() { - override fun equals(other: Any?): Boolean { - return (other is IOException) - } - } - ) - ), - responseObject - ) - } -} diff --git a/app/src/test/kotlin/at/connyduck/pixelcat/network/calladapter/NetworkResponseAdapterFactoryTest.kt b/app/src/test/kotlin/at/connyduck/pixelcat/network/calladapter/NetworkResponseAdapterFactoryTest.kt deleted file mode 100644 index f5e9ec8..0000000 --- a/app/src/test/kotlin/at/connyduck/pixelcat/network/calladapter/NetworkResponseAdapterFactoryTest.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2020 Conny Duck - * - * This file is part of Pixelcat. - * - * Pixelcat is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelcat is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package at.connyduck.pixelcat.network.calladapter - -import com.squareup.moshi.Types -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertNull -import org.junit.jupiter.api.Test -import retrofit2.Call -import retrofit2.Retrofit - -class NetworkResponseAdapterFactoryTest { - - private val retrofit = Retrofit.Builder().baseUrl("http://example.com").build() - - @Test - fun `should return a NetworkResponseCallAdapter when the type is supported`() { - val networkResponseType = - Types.newParameterizedType(NetworkResponse::class.java, TestResponseClass::class.java) - val callType = Types.newParameterizedType(Call::class.java, networkResponseType) - - val adapter = NetworkResponseAdapterFactory().get(callType, arrayOf(), retrofit) - - assertEquals(TestResponseClass::class.java, adapter?.responseType()) - } - - @Test - fun `should return null if the type is not supported`() { - - val adapter = NetworkResponseAdapterFactory().get(TestResponseClass::class.java, arrayOf(), retrofit) - - assertNull(adapter) - } -} diff --git a/app/src/test/kotlin/at/connyduck/pixelcat/network/calladapter/NetworkResponseCallTest.kt b/app/src/test/kotlin/at/connyduck/pixelcat/network/calladapter/NetworkResponseCallTest.kt deleted file mode 100644 index 1151ed9..0000000 --- a/app/src/test/kotlin/at/connyduck/pixelcat/network/calladapter/NetworkResponseCallTest.kt +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2020 Conny Duck - * - * This file is part of Pixelcat. - * - * Pixelcat is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelcat is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package at.connyduck.pixelcat.network.calladapter - -import okhttp3.ResponseBody.Companion.toResponseBody -import org.junit.Assert.assertEquals -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response -import java.io.IOException - -class NetworkResponseCallTest { - - private val backingCall = TestCall() - private val networkCall = NetworkResponseCall(backingCall) - - @Test - fun `should throw an error when invoking 'execute'`() { - assertThrows { - networkCall.execute() - } - } - - @Test - fun `should delegate properties to backing call`() { - with(networkCall) { - assertEquals(isExecuted, backingCall.isExecuted) - assertEquals(isCanceled, backingCall.isCanceled) - assertEquals(request(), backingCall.request()) - } - } - - @Test - fun `should return new instance when cloned`() { - val clonedCall = networkCall.clone() - assert(clonedCall !== networkCall) - } - - @Test - fun `should cancel backing call as well when cancelled`() { - networkCall.cancel() - assert(backingCall.isCanceled) - } - - @Test - fun `should parse successful call as NetworkResponse Success`() { - val body = "Test body" - networkCall.enqueue( - object : Callback> { - override fun onResponse( - call: Call>, - response: Response> - ) { - assertTrue(response.isSuccessful) - assertEquals( - response.body(), - NetworkResponse.Success(body) - ) - } - - override fun onFailure(call: Call>, t: Throwable) { - throw IllegalStateException() - } - } - ) - backingCall.complete(body) - } - - @Test - fun `should parse call with 404 error code as ApiError`() { - val errorCode = 404 - val errorBody = "not found" - networkCall.enqueue( - object : Callback> { - override fun onResponse( - call: Call>, - response: Response> - ) { - assertEquals( - response.body(), - NetworkResponse.Failure(NetworkResponseError.ApiError(errorCode)) - ) - } - - override fun onFailure(call: Call>, t: Throwable) { - throw IllegalStateException() - } - } - ) - - backingCall.complete(Response.error(errorCode, errorBody.toResponseBody())) - } - - @Test - fun `should parse call with IOException as NetworkError`() { - val exception = IOException() - networkCall.enqueue( - object : Callback> { - override fun onResponse( - call: Call>, - response: Response> - ) { - assertEquals( - response.body(), - NetworkResponse.Failure(NetworkResponseError.NetworkError(exception)) - ) - } - - override fun onFailure(call: Call>, t: Throwable) { - throw IllegalStateException() - } - } - ) - - backingCall.completeWithException(exception) - } -} diff --git a/app/src/test/kotlin/at/connyduck/pixelcat/network/calladapter/TestApi.kt b/app/src/test/kotlin/at/connyduck/pixelcat/network/calladapter/TestApi.kt deleted file mode 100644 index fb97084..0000000 --- a/app/src/test/kotlin/at/connyduck/pixelcat/network/calladapter/TestApi.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2020 Conny Duck - * - * This file is part of Pixelcat. - * - * Pixelcat is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelcat is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package at.connyduck.pixelcat.network.calladapter - -import retrofit2.http.GET - -interface TestApi { - - @GET("testpath") - suspend fun testEndpoint(): NetworkResponse -} diff --git a/app/src/test/kotlin/at/connyduck/pixelcat/network/calladapter/TestCall.kt b/app/src/test/kotlin/at/connyduck/pixelcat/network/calladapter/TestCall.kt deleted file mode 100644 index 44e399e..0000000 --- a/app/src/test/kotlin/at/connyduck/pixelcat/network/calladapter/TestCall.kt +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2020 Conny Duck - * - * This file is part of Pixelcat. - * - * Pixelcat is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelcat is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package at.connyduck.pixelcat.network.calladapter - -import okhttp3.Request -import okio.Timeout -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response -import java.io.InterruptedIOException - -class TestCall : Call { - private var executed = false - private var canceled = false - private var callback: Callback? = null - private var request = Request.Builder().url("http://example.com").build() - - fun completeWithException(t: Throwable) { - synchronized(this) { - callback?.onFailure(this, t) - } - } - - fun complete(body: T) = complete(Response.success(body)) - - fun complete(response: Response) { - synchronized(this) { - callback?.onResponse(this, response) - } - } - - override fun enqueue(callback: Callback) { - synchronized(this) { - this.callback = callback - } - } - - override fun isExecuted() = synchronized(this) { executed } - override fun isCanceled() = synchronized(this) { canceled } - override fun clone() = TestCall() - - override fun cancel() { - synchronized(this) { - if (canceled) return - canceled = true - - val exception = InterruptedIOException("canceled") - callback?.onFailure(this, exception) - } - } - - override fun execute(): Response { - throw UnsupportedOperationException("Network call does not support synchronous execution") - } - - override fun request() = request - override fun timeout() = Timeout() -} diff --git a/app/src/test/kotlin/at/connyduck/pixelcat/network/calladapter/TestResponseClass.kt b/app/src/test/kotlin/at/connyduck/pixelcat/network/calladapter/TestResponseClass.kt deleted file mode 100644 index 861f4da..0000000 --- a/app/src/test/kotlin/at/connyduck/pixelcat/network/calladapter/TestResponseClass.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2020 Conny Duck - * - * This file is part of Pixelcat. - * - * Pixelcat is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelcat is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package at.connyduck.pixelcat.network.calladapter - -data class TestResponseClass( - val lets: String, - val test: Int -) diff --git a/build.gradle.kts b/build.gradle.kts index a53c1e5..e6b29b0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,6 +14,7 @@ buildscript { allprojects { repositories { google() + mavenCentral() jcenter() maven(url = "https://jitpack.io") }