Skip to content

Commit

Permalink
Merge pull request #86 from matheus-corregiari/release/1.3.0
Browse files Browse the repository at this point in the history
Release/1.3.0
  • Loading branch information
matheus-corregiari authored May 29, 2024
2 parents a7e01a2 + 2e9d238 commit ae60da8
Show file tree
Hide file tree
Showing 50 changed files with 106 additions and 74 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ jobs:
uses: ./.github/actions/setup-gradle

- name: Unit Tests ${{ matrix.module }}
run: ./gradlew :${{ matrix.module }}:testDebugUnitTest
run: ./gradlew :${{ matrix.module }}:testDebugUnitTest --no-parallel

- name: Kover ${{ matrix.module }}
run: ./gradlew :${{ matrix.module }}:koverVerify
Expand Down
41 changes: 13 additions & 28 deletions toolkit/event-observer/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,36 +1,21 @@
import com.toolkit.plugin.multiplatform.androidMain
import com.toolkit.plugin.multiplatform.androidUnitTest

plugins {
id("toolkit-multiplatform-library")
id("toolkit-android-library")
id("toolkit-publish")
}

android.namespace = "br.com.arch.toolkit.livedata"

kotlin {
sourceSets {

// Libraries
androidMain {
dependencies {
implementation(libraries.jetbrains.stdlib.jdk8)
implementation(libraries.jetbrains.coroutines.core)
implementation(libraries.jetbrains.coroutines.android)
implementation(libraries.androidx.lifecycle.livedata)
}
}
dependencies {
// Libraries
implementation(libraries.jetbrains.stdlib.jdk8)
implementation(libraries.jetbrains.coroutines.core)
implementation(libraries.jetbrains.coroutines.android)
implementation(libraries.androidx.lifecycle.livedata)

// Test Libraries
androidUnitTest {
dependencies {
implementation(libraries.androidx.lifecycle.livedata)
implementation(libraries.androidx.lifecycle.runtime)
implementation(libraries.androidx.test.core)
implementation(libraries.jetbrains.test.coroutines)
implementation(libraries.mockito.test.core)
implementation(libraries.mockito.test.kotlin)
}
}
}
// Test Libraries
testImplementation(libraries.androidx.lifecycle.runtime)
testImplementation(libraries.androidx.test.core)
testImplementation(libraries.jetbrains.test.coroutines)
testImplementation(libraries.mockito.test.core)
testImplementation(libraries.mockito.test.kotlin)
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ data class DataResult<T>(
*/
private var scope: CoroutineScope? = null
fun scope(scope: CoroutineScope) = apply { this.scope = scope }
fun scope(scope: CoroutineDispatcher) = apply { this.scope = CoroutineScope(scope) }

/**
* Scope to run all the transformations, it is optional, is null, will use the default inside ObserveWrapper
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package br.com.arch.toolkit.result
import androidx.annotation.NonNull
import androidx.annotation.Nullable
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.Observer
import br.com.arch.toolkit.annotation.Experimental
Expand Down Expand Up @@ -1055,7 +1056,7 @@ class ObserveWrapper<T> internal constructor() {
val observer = object : Observer<DataResult<T>?> {
override fun onChanged(value: DataResult<T>?) {
scope.launchWithErrorTreatment {
handleResult(value)
handleResult(value) { owner.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED) }
if (eventList.isEmpty()) {
liveData.removeObserver(this)
}
Expand Down Expand Up @@ -1094,64 +1095,67 @@ class ObserveWrapper<T> internal constructor() {
}
//endregion

private suspend fun handleResult(@Nullable result: DataResult<T>?) {
private suspend fun handleResult(
@Nullable result: DataResult<T>?,
evaluateBeforeDispatch: suspend () -> Boolean = { true }
) {
if (result == null) return
val isLoading = result.status == LOADING

eventList.iterate(result) { event ->
return@iterate when {
// Handle None
result.status == NONE -> (event as? NoneEvent)?.wrapper
?.handle(null, transformDispatcher) == true
?.handle(null, transformDispatcher, evaluateBeforeDispatch) == true

// Handle Loading
event is LoadingEvent -> event.run {
wrapper.handle(isLoading, transformDispatcher) && isLoading.not()
wrapper.handle(isLoading, transformDispatcher, evaluateBeforeDispatch) && isLoading.not()
}

// Handle ShowLoading
event is ShowLoadingEvent && isLoading -> event.run {
wrapper.handle(true, transformDispatcher)
wrapper.handle(true, transformDispatcher, evaluateBeforeDispatch)
}

// Handle HideLoading
event is HideLoadingEvent && isLoading.not() -> event.run {
wrapper.handle(isLoading, transformDispatcher)
wrapper.handle(isLoading, transformDispatcher, evaluateBeforeDispatch)
}

// Handle Error
event is ErrorEvent && result.status == ERROR -> event.run {
wrapper.handle(result.error, transformDispatcher)
wrapper.handle(result.error, transformDispatcher, evaluateBeforeDispatch)
}

// Handle Success
event is SuccessEvent && result.status == SUCCESS -> event.run {
wrapper.handle(null, transformDispatcher)
wrapper.handle(null, transformDispatcher, evaluateBeforeDispatch)
}

// Handle Data
event is DataEvent -> (event as DataEvent<T>).wrapper.let {
it.handle(result.data, transformDispatcher) && (result.data != null)
it.handle(result.data, transformDispatcher, evaluateBeforeDispatch) && (result.data != null)
}

// Handle Empty
event is EmptyEvent && result.isListType && result.isEmpty -> event.run {
wrapper.handle(null, transformDispatcher)
wrapper.handle(null, transformDispatcher, evaluateBeforeDispatch)
}

// Handle Not Empty
event is NotEmptyEvent && result.isListType && result.isNotEmpty -> event.run {
wrapper.handle(null, transformDispatcher)
wrapper.handle(null, transformDispatcher, evaluateBeforeDispatch)
}

// Handle Result
event is ResultEvent<*> -> (event as ResultEvent<T>).run {
wrapper.handle(result, transformDispatcher)
wrapper.handle(result, transformDispatcher, evaluateBeforeDispatch)
}

// Handle Status
event is StatusEvent -> event.run {
wrapper.handle(result.status, transformDispatcher)
wrapper.handle(result.status, transformDispatcher, evaluateBeforeDispatch)
}

else -> false
Expand Down Expand Up @@ -1231,10 +1235,17 @@ internal class WrapObserver<T, V>(
@Nullable val observer: (suspend (T) -> Unit)? = null,
@Nullable val emptyObserver: (suspend () -> Unit)? = null,
@Nullable val transformer: (suspend (T) -> V)? = null,
@Nullable val transformerObserver: (suspend (V) -> Unit)? = null
@Nullable val transformerObserver: (suspend (V) -> Unit)? = null,
) {

suspend fun handle(@Nullable data: T?, dispatcher: CoroutineDispatcher) = when {
suspend fun handle(
@Nullable data: T?,
dispatcher: CoroutineDispatcher,
evaluate: suspend () -> Boolean
) = when {

evaluate.invoke().not() -> false

emptyObserver != null -> {
emptyObserver.invoke()
true
Expand All @@ -1245,14 +1256,15 @@ internal class WrapObserver<T, V>(
true
}

data != null -> executeTransformer(data, dispatcher)
data != null -> executeTransformer(data, dispatcher, evaluate)

else -> false
}

private suspend fun executeTransformer(
@Nullable data: T,
dispatcher: CoroutineDispatcher
dispatcher: CoroutineDispatcher,
evaluate: suspend () -> Boolean
) = when {
transformerObserver == null -> false
transformer == null -> false
Expand All @@ -1263,15 +1275,20 @@ internal class WrapObserver<T, V>(

val catch = CoroutineExceptionHandler { _, error -> throw error }
withContext(currentCoroutineContext() + catch) {
result.onSuccess { transformerObserver.invoke(it) }
.onFailure {
if (evaluate.invoke()) {
result.onSuccess {
transformerObserver.invoke(it)
}.onFailure {
throw DataResultTransformationException(
"Error performing transformation",
it
)
}
true
} else {
false
}
}
true
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,15 @@ internal fun <T> ResponseLiveData<T>.testSetValue(value: DataResult<T>?) {
it.invoke(this, value)
}
}

internal fun enableExceptionCheck() {
Class.forName("kotlinx.coroutines.test.TestScopeKt")
.getDeclaredMethod("setCatchNonTestRelatedExceptions", Boolean::class.java)
.invoke(null, false)
}

internal fun disableExceptionCheck() {
Class.forName("kotlinx.coroutines.test.TestScopeKt")
.getDeclaredMethod("setCatchNonTestRelatedExceptions", Boolean::class.java)
.invoke(null, true)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,25 @@

package br.com.arch.toolkit.result

import br.com.arch.toolkit.disableExceptionCheck
import br.com.arch.toolkit.enableExceptionCheck
import br.com.arch.toolkit.exception.DataResultException
import br.com.arch.toolkit.exception.DataResultTransformationException
import br.com.arch.toolkit.util.dataResultError
import br.com.arch.toolkit.util.dataResultSuccess
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Assert
import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
import org.junit.runners.MethodSorters
import org.mockito.kotlin.any
Expand All @@ -25,26 +32,36 @@ import org.mockito.kotlin.verifyBlocking
import org.mockito.kotlin.verifyNoInteractions
import org.mockito.kotlin.whenever

/**
* Yes I do hate the person who did the coroutines stopping treating exceptions inside the scope of the test
* without giving me any other option to do that
*/
@OptIn(ExperimentalCoroutinesApi::class)
@Ignore("this test can only be run manually, because it will only work if it's the first test to run.")
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class ObserveWrapperTest_ThrownError {

private val error = IllegalStateException("Thrown Error!")
private val expected = DataResultException(
message = "Any error event found, please add one error { ... } to retry",
error = error
message = "Any error event found, please add one error { ... } to retry", error = error
)
private val expectedTransformation = DataResultTransformationException(
message = "Error performing transformation",
error = error
message = "Error performing transformation", error = error
)
private val expectedError = DataResultException(
message = "Error retried but without any success",
error = error
message = "Error retried but without any success", error = error
)

init {
Dispatchers.setMain(StandardTestDispatcher())
@Before
fun setUp() {
Dispatchers.setMain(StandardTestDispatcher(TestCoroutineScheduler()))
enableExceptionCheck()
}

@After
fun tearDown() {
Dispatchers.resetMain()
disableExceptionCheck()
}

//region Data Error Thrown Scenarios
Expand Down
34 changes: 17 additions & 17 deletions tools/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@ build-sdk-target = "34"
build-tools = "34.0.0"

## JetBrains
jetbrains-kotlin = "1.9.20"
jetbrains-coroutines = "1.7.3" # Quebra a parte de tratamento de exception no ObserveWrapper
jetbrains-kotlin = "1.9.23"
jetbrains-coroutines = "1.8.0" # Quebra a parte de tratamento de exception no ObserveWrapper
jetbrains-kover = "0.7.4"

## AndroidX
androidx-appcompat = "1.6.1"
androidx-annotation = "1.7.1"
androidx-annotation = "1.8.0"
androidx-constraint = "2.2.0-alpha13"
androidx-recyclerview = "1.3.2"
androidx-lifecycle-livedata = "2.7.0"
androidx-lifecycle-runtime = "2.7.0"
androidx-lifecycle-livedata = "2.8.0"
androidx-lifecycle-runtime = "2.8.0"
androidx-window = "1.2.0"
androidx-splash = "1.0.1"
androidx-test-core = "2.2.0"
Expand All @@ -37,12 +37,12 @@ androidx-test-espresso = "3.5.1"

## AndroidX Compose
androidx-compose-compiler = "1.4.3"
androidx-compose-material = "1.6.2"
androidx-compose-material3 = "1.2.0"
androidx-compose-material = "1.6.7"
androidx-compose-material3 = "1.2.1"
androidx-compose-constraint = "1.0.1"
androidx-compose-core = "1.6.2"
androidx-compose-lifecycle = "2.7.0"
androidx-compose-activity = "1.8.2"
androidx-compose-core = "1.6.7"
androidx-compose-lifecycle = "2.8.0"
androidx-compose-activity = "1.9.0"

## SquareUp
square-retrofit = "2.9.0"
Expand All @@ -51,8 +51,8 @@ square-moshi = "1.14.0"
square-timber = "5.0.1"

## Google
google-material = "1.11.0"
google-ksp = "1.9.10-1.0.13"
google-material = "1.12.0"
google-ksp = "1.9.23-1.0.20"

## Tools
mockito = "5.6.0"
Expand Down Expand Up @@ -119,16 +119,16 @@ detekt-formatting = { group = "io.gitlab.arturbosch.detekt", name = "detekt-form
# ------------------------------------------------------------------------------------------------ #
## Comment this when you start to update some libraries
x-normalize-001 = { group = "androidx.activity", name = "activity", version.ref = "androidx-compose-activity" }
x-normalize-002 = { group = "androidx.annotation", name = "annotation-experimental", version = "1.4.0" }
x-normalize-002 = { group = "androidx.annotation", name = "annotation-experimental", version = "1.4.1" }
x-normalize-003 = { group = "androidx.arch.core", name = "core-common", version = "2.2.0" }
x-normalize-004 = { group = "androidx.arch.core", name = "core-runtime", version = "2.2.0" }
x-normalize-005 = { group = "androidx.collection", name = "collection", version = "1.4.0" }
x-normalize-006 = { group = "androidx.core", name = "core", version = "1.12.0" }
x-normalize-007 = { group = "androidx.core", name = "core-ktx", version = "1.12.0" }
x-normalize-006 = { group = "androidx.core", name = "core", version = "1.13.1" }
x-normalize-007 = { group = "androidx.core", name = "core-ktx", version = "1.13.1" }
x-normalize-008 = { group = "androidx.customview", name = "customview", version = "1.1.0" }
x-normalize-009 = { group = "androidx.drawerlayout", name = "drawerlayout", version = "1.2.0" }
x-normalize-010 = { group = "androidx.lifecycle", name = "lifecycle-common", version = "2.7.0" }
x-normalize-011 = { group = "androidx.viewpager2", name = "viewpager2", version = "1.1.0-beta02" }
x-normalize-010 = { group = "androidx.lifecycle", name = "lifecycle-common", version = "2.8.0" }
x-normalize-011 = { group = "androidx.viewpager2", name = "viewpager2", version = "1.1.0" }
x-normalize-012 = { group = "androidx.test", name = "monitor", version = "1.6.1" }
x-normalize-013 = { group = "androidx.test", name = "runner", version = "1.5.2" }
x-normalize-014 = { group = "com.google.auto.value", name = "auto-value-annotations", version = "1.9" }
Expand Down

0 comments on commit ae60da8

Please sign in to comment.