Coverage Summary for Class: NetworkEitherSuspendCall (com.javiersc.network.either.internal.suspend)

Class Method, % Branch, % Line, % Instruction, %
NetworkEitherSuspendCall 22.2% (2/9) 53.3% (8/15) 30.9% (38/123)
NetworkEitherSuspendCall$enqueue$1$1 100% (3/3) 66.7% (12/18) 81.8% (18/22) 66.4% (79/119)
Total 41.7% (5/12) 66.7% (12/18) 70.3% (26/37) 48.3% (117/242)


 package com.javiersc.network.either.internal.suspend
 
 import com.javiersc.network.either.NetworkEither
 import com.javiersc.network.either.NetworkEither.Companion.localFailure
 import com.javiersc.network.either.NetworkEither.Companion.remoteFailure
 import com.javiersc.network.either.NetworkEither.Companion.success
 import com.javiersc.network.either.NetworkEither.Companion.unknownFailure
 import com.javiersc.network.either.internal.hasBody
 import com.javiersc.network.either.internal.utils.emptyHeader
 import com.javiersc.network.either.internal.utils.printlnError
 import com.javiersc.network.either.internal.utils.printlnWarning
 import io.ktor.http.HttpStatusCode.Companion.NoContent
 import io.ktor.util.toMap
 import java.io.EOFException
 import java.io.InterruptedIOException
 import java.net.ConnectException
 import java.net.UnknownHostException
 import kotlinx.serialization.SerializationException
 import okhttp3.Request
 import okhttp3.ResponseBody
 import okio.Timeout
 import retrofit2.Call
 import retrofit2.Callback
 import retrofit2.Converter
 import retrofit2.HttpException
 import retrofit2.Response
 
 internal class NetworkEitherSuspendCall<F : Any, S : Any>(
     private val backingCall: Call<S>,
     private val errorConverter: Converter<ResponseBody, F>,
     private val isNetworkAvailable: () -> Boolean,
 ) : Call<NetworkEither<F, S>> {
 
     override fun enqueue(callback: Callback<NetworkEither<F, S>>) =
         synchronized(this) {
             backingCall.enqueue(
                 object : Callback<S> {
                     override fun onResponse(call: Call<S>, response: Response<S>) {
                         response.responseSuspendHandler(
                             errorConverter,
                             this@NetworkEitherSuspendCall,
                             callback
                         )
                     }
 
                     override fun onFailure(call: Call<S>, throwable: Throwable) {
                         if (!isNetworkAvailable()) {
                             onCommonConnectionExceptions(callback, isNetworkAvailable())
                         } else {
                             when (throwable) {
                                 is UnknownHostException,
                                 is ConnectException,
                                 is InterruptedIOException -> {
                                     onCommonConnectionExceptions(callback, isNetworkAvailable())
                                 }
                                 is EOFException -> onEOFException(callback)
                                 is IllegalStateException -> {
                                     onIllegalStateException(callback, throwable)
                                 }
                                 is HttpException -> {
                                     onHttpException(callback, errorConverter, throwable)
                                 }
                                 is SerializationException -> {
                                     if (throwable.hasBody) {
                                         onIllegalStateException(callback, throwable)
                                     } else {
                                         onEOFException(callback)
                                     }
                                 }
                                 else -> {
                                     Response.success(unknownFailure(throwable))
                                 }
                             }
                         }
                     }
                 }
             )
         }
 
     override fun isExecuted(): Boolean = synchronized(this) { backingCall.isExecuted }
 
     override fun clone(): Call<NetworkEither<F, S>> =
         NetworkEitherSuspendCall(backingCall.clone(), errorConverter, isNetworkAvailable)
 
     override fun isCanceled(): Boolean = synchronized(this) { backingCall.isCanceled }
 
     override fun cancel() = synchronized(this) { backingCall.cancel() }
 
     override fun execute(): Response<NetworkEither<F, S>> =
         throw UnsupportedOperationException("Suspend call does not support synchronous execution")
 
     override fun request(): Request = backingCall.request()
 
     override fun timeout(): Timeout = backingCall.timeout()
 }
 
 private fun <F, S> Call<NetworkEither<F, S>>.onEOFException(
     callback: Callback<NetworkEither<F, S>>
 ) {
     printlnWarning(
         """
            | # # # # # # # # # # # # # # WARNING # # # # # # # # # # # # # # # # # # #
            | # Every 2XX response should have a body except 204/205, as the response #
            | # was empty, the response is transformed to Success with code 204 and   #
            | # the headers are lost. The type should be Unit.                        #
            | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
         """
             .trimMargin()
     )
 
     @Suppress("UNCHECKED_CAST")
     try {
         val success = success(Unit as S, NoContent.value, emptyHeader.toMap())
         callback.onResponse(this, Response.success(success))
     } catch (e: ClassCastException) {
         printlnError(
             """
                | # # # # # # # # # # # # # # ERROR # # # # # # # # # # # # # # # # # #
                | # NetworkResponse should use Unit as Success type when body is null #
                | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
             """
                 .trimMargin()
         )
         callback.onResponse(this, Response.success(unknownFailure(e)))
     }
 }
 
 private fun <F, S> Call<NetworkEither<F, S>>.onIllegalStateException(
     callback: Callback<NetworkEither<F, S>>,
     throwable: Throwable,
 ) {
     printlnError(
         """
            | # # # # # # # # # # # # # # ERROR # # # # # # # # # # # # # # #
            | # Response body can't be serialized with the object provided  #
            | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
         """
             .trimMargin()
     )
     callback.onResponse(this, Response.success(unknownFailure(throwable)))
 }
 
 private fun <F : Any, S : Any> NetworkEitherSuspendCall<F, S>.onCommonConnectionExceptions(
     callback: Callback<NetworkEither<F, S>>,
     isNetworkAvailable: Boolean,
 ) {
     callback.onResponse(
         this,
         if (isNetworkAvailable) Response.success(remoteFailure())
         else Response.success(localFailure())
     )
 }
 
 private fun <F : Any, S : Any> NetworkEitherSuspendCall<F, S>.onHttpException(
     callback: Callback<NetworkEither<F, S>>,
     errorConverter: Converter<ResponseBody, F>,
     exception: HttpException,
 ) = exception.httpExceptionSuspendHandler(errorConverter, this, callback)