diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ad1e48d7..2517d228 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -6,6 +6,9 @@ on:
pull_request:
branches: [ master ]
+env:
+ CI: true
+
jobs:
build:
runs-on: ubuntu-latest
diff --git a/.github/workflows/review-suggest.yml b/.github/workflows/review-suggest.yml
index d06d0ddd..444bdfee 100644
--- a/.github/workflows/review-suggest.yml
+++ b/.github/workflows/review-suggest.yml
@@ -2,6 +2,10 @@ name: reviewdog-suggester
on:
pull_request:
types: [opened, synchronize, reopened]
+
+env:
+ CI: true
+
jobs:
kotlin:
name: runner / suggester / spotless
diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml
index 9ad2da3b..1615b60c 100644
--- a/.github/workflows/unit-test.yml
+++ b/.github/workflows/unit-test.yml
@@ -6,6 +6,9 @@ on:
pull_request:
branches: [ master ]
+env:
+ CI: true
+
jobs:
build:
runs-on: ubuntu-latest
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index ce97a058..b9c4f02c 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -14,6 +14,7 @@
+
@@ -21,10 +22,11 @@
+
-
\ No newline at end of file
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 076b3e47..74a53cfc 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -9,6 +9,7 @@
+
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index f73e6abe..6d8e5bc6 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -59,6 +59,7 @@ dependencies {
implementation(domain)
implementation(data)
implementation(core)
+ implementation(coreUi)
implementation(featureMain)
implementation(featureAdd)
implementation(featureSearch)
diff --git a/app/src/main/java/com/hoc/flowmvi/core/CoreModule.kt b/app/src/main/java/com/hoc/flowmvi/core/CoreModule.kt
index 088c9ac8..bacf7866 100644
--- a/app/src/main/java/com/hoc/flowmvi/core/CoreModule.kt
+++ b/app/src/main/java/com/hoc/flowmvi/core/CoreModule.kt
@@ -1,7 +1,7 @@
package com.hoc.flowmvi.core
import com.hoc.flowmvi.core.dispatchers.CoroutineDispatchers
-import com.hoc.flowmvi.core.navigator.Navigator
+import com.hoc.flowmvi.core_ui.navigator.Navigator
import org.koin.dsl.module
val coreModule = module {
diff --git a/app/src/main/java/com/hoc/flowmvi/core/NavigatorImpl.kt b/app/src/main/java/com/hoc/flowmvi/core/NavigatorImpl.kt
index 130f6c2d..0bfd900e 100644
--- a/app/src/main/java/com/hoc/flowmvi/core/NavigatorImpl.kt
+++ b/app/src/main/java/com/hoc/flowmvi/core/NavigatorImpl.kt
@@ -1,8 +1,8 @@
package com.hoc.flowmvi.core
import android.content.Context
-import com.hoc.flowmvi.core.navigator.IntentProviders
-import com.hoc.flowmvi.core.navigator.Navigator
+import com.hoc.flowmvi.core_ui.navigator.IntentProviders
+import com.hoc.flowmvi.core_ui.navigator.Navigator
class NavigatorImpl(
private val add: IntentProviders.Add,
diff --git a/buildSrc/src/main/kotlin/deps.kt b/buildSrc/src/main/kotlin/deps.kt
index 1cbfc9cf..3e969885 100644
--- a/buildSrc/src/main/kotlin/deps.kt
+++ b/buildSrc/src/main/kotlin/deps.kt
@@ -1,5 +1,6 @@
@file:Suppress("unused", "ClassName", "SpellCheckingInspection")
+import org.gradle.api.Project
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.kotlin.dsl.project
import org.gradle.plugin.use.PluginDependenciesSpec
@@ -97,12 +98,14 @@ inline val PDsS.kotlinKapt: PDS get() = id("kotlin-kapt")
inline val DependencyHandler.domain get() = project(":domain")
inline val DependencyHandler.core get() = project(":core")
+inline val DependencyHandler.coreUi get() = project(":core-ui")
inline val DependencyHandler.data get() = project(":data")
inline val DependencyHandler.featureMain get() = project(":feature-main")
inline val DependencyHandler.featureAdd get() = project(":feature-add")
inline val DependencyHandler.featureSearch get() = project(":feature-search")
inline val DependencyHandler.mviBase get() = project(":mvi-base")
inline val DependencyHandler.mviTesting get() = project(":mvi-testing")
+inline val DependencyHandler.testUtils get() = project(":test-utils")
fun DependencyHandler.addUnitTest(testImplementation: Boolean = true) {
val configName = if (testImplementation) "testImplementation" else "implementation"
@@ -112,3 +115,8 @@ fun DependencyHandler.addUnitTest(testImplementation: Boolean = true) {
add(configName, deps.test.kotlinJUnit)
add(configName, deps.coroutines.test)
}
+
+val Project.isCiBuild: Boolean
+ get() = providers.environmentVariable("CI")
+ .forUseAtConfigurationTime()
+ .orNull == "true"
diff --git a/core-ui/.gitignore b/core-ui/.gitignore
new file mode 100644
index 00000000..796b96d1
--- /dev/null
+++ b/core-ui/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/core-ui/build.gradle.kts b/core-ui/build.gradle.kts
new file mode 100644
index 00000000..019ff854
--- /dev/null
+++ b/core-ui/build.gradle.kts
@@ -0,0 +1,55 @@
+plugins {
+ androidLib
+ kotlinAndroid
+}
+
+android {
+ compileSdk = appConfig.compileSdkVersion
+ buildToolsVersion = appConfig.buildToolsVersion
+
+ defaultConfig {
+ minSdk = appConfig.minSdkVersion
+ targetSdk = appConfig.targetSdkVersion
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles("consumer-rules.pro")
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+ kotlinOptions { jvmTarget = JavaVersion.VERSION_1_8.toString() }
+
+ testOptions {
+ unitTests.isIncludeAndroidResources = true
+ unitTests.isReturnDefaultValues = true
+ }
+}
+
+dependencies {
+ implementation(deps.coroutines.core)
+ implementation(deps.coroutines.android)
+
+ implementation(deps.androidx.coreKtx)
+ implementation(deps.androidx.swipeRefreshLayout)
+ implementation(deps.androidx.recyclerView)
+ implementation(deps.androidx.material)
+
+ implementation(deps.lifecycle.commonJava8)
+ implementation(deps.lifecycle.runtimeKtx)
+
+ implementation(deps.timber)
+
+ addUnitTest()
+}
diff --git a/core/consumer-rules.pro b/core-ui/consumer-rules.pro
similarity index 100%
rename from core/consumer-rules.pro
rename to core-ui/consumer-rules.pro
diff --git a/core/proguard-rules.pro b/core-ui/proguard-rules.pro
similarity index 100%
rename from core/proguard-rules.pro
rename to core-ui/proguard-rules.pro
diff --git a/core-ui/src/androidTest/java/com/hoc/flowmvi/core_ui/ExampleInstrumentedTest.kt b/core-ui/src/androidTest/java/com/hoc/flowmvi/core_ui/ExampleInstrumentedTest.kt
new file mode 100644
index 00000000..bd932593
--- /dev/null
+++ b/core-ui/src/androidTest/java/com/hoc/flowmvi/core_ui/ExampleInstrumentedTest.kt
@@ -0,0 +1 @@
+package com.hoc.flowmvi.core_ui
diff --git a/core/src/main/AndroidManifest.xml b/core-ui/src/main/AndroidManifest.xml
similarity index 87%
rename from core/src/main/AndroidManifest.xml
rename to core-ui/src/main/AndroidManifest.xml
index baf271f3..069f7323 100644
--- a/core/src/main/AndroidManifest.xml
+++ b/core-ui/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
+ package="com.hoc.flowmvi.core_ui">
diff --git a/core/src/main/java/com/hoc/flowmvi/core/CollectIn.kt b/core-ui/src/main/java/com/hoc/flowmvi/core_ui/CollectIn.kt
similarity index 97%
rename from core/src/main/java/com/hoc/flowmvi/core/CollectIn.kt
rename to core-ui/src/main/java/com/hoc/flowmvi/core_ui/CollectIn.kt
index c80ff5fe..669e251d 100644
--- a/core/src/main/java/com/hoc/flowmvi/core/CollectIn.kt
+++ b/core-ui/src/main/java/com/hoc/flowmvi/core_ui/CollectIn.kt
@@ -1,4 +1,4 @@
-package com.hoc.flowmvi.core
+package com.hoc.flowmvi.core_ui
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
diff --git a/core/src/main/java/com/hoc/flowmvi/core/FlowBinding.kt b/core-ui/src/main/java/com/hoc/flowmvi/core_ui/FlowBinding.kt
similarity index 99%
rename from core/src/main/java/com/hoc/flowmvi/core/FlowBinding.kt
rename to core-ui/src/main/java/com/hoc/flowmvi/core_ui/FlowBinding.kt
index 29528fb0..73d57c17 100644
--- a/core/src/main/java/com/hoc/flowmvi/core/FlowBinding.kt
+++ b/core-ui/src/main/java/com/hoc/flowmvi/core_ui/FlowBinding.kt
@@ -1,4 +1,4 @@
-package com.hoc.flowmvi.core
+package com.hoc.flowmvi.core_ui
import android.content.Context
import android.os.Looper
diff --git a/core/src/main/java/com/hoc/flowmvi/core/SwipeLeftToDeleteCallback.kt b/core-ui/src/main/java/com/hoc/flowmvi/core_ui/SwipeLeftToDeleteCallback.kt
similarity index 98%
rename from core/src/main/java/com/hoc/flowmvi/core/SwipeLeftToDeleteCallback.kt
rename to core-ui/src/main/java/com/hoc/flowmvi/core_ui/SwipeLeftToDeleteCallback.kt
index 9c68ce9f..71ae822e 100644
--- a/core/src/main/java/com/hoc/flowmvi/core/SwipeLeftToDeleteCallback.kt
+++ b/core-ui/src/main/java/com/hoc/flowmvi/core_ui/SwipeLeftToDeleteCallback.kt
@@ -1,4 +1,4 @@
-package com.hoc.flowmvi.core
+package com.hoc.flowmvi.core_ui
import android.content.Context
import android.graphics.Canvas
diff --git a/core/src/main/java/com/hoc/flowmvi/core/navigator/Navigator.kt b/core-ui/src/main/java/com/hoc/flowmvi/core_ui/navigator/Navigator.kt
similarity index 88%
rename from core/src/main/java/com/hoc/flowmvi/core/navigator/Navigator.kt
rename to core-ui/src/main/java/com/hoc/flowmvi/core_ui/navigator/Navigator.kt
index 4749440c..d0a6b1db 100644
--- a/core/src/main/java/com/hoc/flowmvi/core/navigator/Navigator.kt
+++ b/core-ui/src/main/java/com/hoc/flowmvi/core_ui/navigator/Navigator.kt
@@ -1,4 +1,4 @@
-package com.hoc.flowmvi.core.navigator
+package com.hoc.flowmvi.core_ui.navigator
import android.content.Context
import android.content.Intent
diff --git a/core/src/main/res/drawable/ic_baseline_delete_white_24.xml b/core-ui/src/main/res/drawable/ic_baseline_delete_white_24.xml
similarity index 100%
rename from core/src/main/res/drawable/ic_baseline_delete_white_24.xml
rename to core-ui/src/main/res/drawable/ic_baseline_delete_white_24.xml
diff --git a/core/src/main/res/font/noto_sans.xml b/core-ui/src/main/res/font/noto_sans.xml
similarity index 100%
rename from core/src/main/res/font/noto_sans.xml
rename to core-ui/src/main/res/font/noto_sans.xml
diff --git a/core/src/main/res/values/colors.xml b/core-ui/src/main/res/values/colors.xml
similarity index 100%
rename from core/src/main/res/values/colors.xml
rename to core-ui/src/main/res/values/colors.xml
diff --git a/core/src/main/res/values/font_certs.xml b/core-ui/src/main/res/values/font_certs.xml
similarity index 100%
rename from core/src/main/res/values/font_certs.xml
rename to core-ui/src/main/res/values/font_certs.xml
diff --git a/core/src/main/res/values/preloaded_fonts.xml b/core-ui/src/main/res/values/preloaded_fonts.xml
similarity index 100%
rename from core/src/main/res/values/preloaded_fonts.xml
rename to core-ui/src/main/res/values/preloaded_fonts.xml
diff --git a/core/src/main/res/values/strings.xml b/core-ui/src/main/res/values/strings.xml
similarity index 100%
rename from core/src/main/res/values/strings.xml
rename to core-ui/src/main/res/values/strings.xml
diff --git a/core/src/main/res/values/styles.xml b/core-ui/src/main/res/values/styles.xml
similarity index 100%
rename from core/src/main/res/values/styles.xml
rename to core-ui/src/main/res/values/styles.xml
diff --git a/core-ui/src/test/java/com/hoc/flowmvi/core_ui/ExampleUnitTest.kt b/core-ui/src/test/java/com/hoc/flowmvi/core_ui/ExampleUnitTest.kt
new file mode 100644
index 00000000..bd932593
--- /dev/null
+++ b/core-ui/src/test/java/com/hoc/flowmvi/core_ui/ExampleUnitTest.kt
@@ -0,0 +1 @@
+package com.hoc.flowmvi.core_ui
diff --git a/core/build.gradle.kts b/core/build.gradle.kts
index 351e1704..cf13ffe4 100644
--- a/core/build.gradle.kts
+++ b/core/build.gradle.kts
@@ -1,55 +1,8 @@
plugins {
- androidLib
- kotlinAndroid
-}
-
-android {
- compileSdk = appConfig.compileSdkVersion
- buildToolsVersion = appConfig.buildToolsVersion
-
- defaultConfig {
- minSdk = appConfig.minSdkVersion
- targetSdk = appConfig.targetSdkVersion
-
- testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
- consumerProguardFiles("consumer-rules.pro")
- }
-
- buildTypes {
- release {
- isMinifyEnabled = true
- proguardFiles(
- getDefaultProguardFile("proguard-android-optimize.txt"),
- "proguard-rules.pro"
- )
- }
- }
-
- compileOptions {
- sourceCompatibility = JavaVersion.VERSION_1_8
- targetCompatibility = JavaVersion.VERSION_1_8
- }
- kotlinOptions { jvmTarget = JavaVersion.VERSION_1_8.toString() }
-
- testOptions {
- unitTests.isIncludeAndroidResources = true
- unitTests.isReturnDefaultValues = true
- }
+ kotlin
}
dependencies {
implementation(deps.coroutines.core)
- implementation(deps.coroutines.android)
-
- implementation(deps.androidx.coreKtx)
- implementation(deps.androidx.swipeRefreshLayout)
- implementation(deps.androidx.recyclerView)
- implementation(deps.androidx.material)
-
- implementation(deps.lifecycle.commonJava8)
- implementation(deps.lifecycle.runtimeKtx)
-
- implementation(deps.timber)
-
addUnitTest()
}
diff --git a/core/src/androidTest/java/com/hoc/flowmvi/core/ExampleInstrumentedTest.kt b/core/src/androidTest/java/com/hoc/flowmvi/core/ExampleInstrumentedTest.kt
deleted file mode 100644
index dc6f3377..00000000
--- a/core/src/androidTest/java/com/hoc/flowmvi/core/ExampleInstrumentedTest.kt
+++ /dev/null
@@ -1 +0,0 @@
-package com.hoc.flowmvi.core
diff --git a/data/build.gradle.kts b/data/build.gradle.kts
index 412a1f59..7bcd2199 100644
--- a/data/build.gradle.kts
+++ b/data/build.gradle.kts
@@ -18,7 +18,7 @@ android {
buildTypes {
release {
- isMinifyEnabled = true
+ isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
@@ -55,4 +55,5 @@ dependencies {
implementation(deps.timber)
addUnitTest()
+ testImplementation(testUtils)
}
diff --git a/data/src/test/java/com/hoc/flowmvi/data/UserRepositoryImplTest.kt b/data/src/test/java/com/hoc/flowmvi/data/UserRepositoryImplTest.kt
index eb0d8e05..18541457 100644
--- a/data/src/test/java/com/hoc/flowmvi/data/UserRepositoryImplTest.kt
+++ b/data/src/test/java/com/hoc/flowmvi/data/UserRepositoryImplTest.kt
@@ -4,12 +4,13 @@ import arrow.core.Either
import arrow.core.getOrHandle
import arrow.core.identity
import com.hoc.flowmvi.core.Mapper
-import com.hoc.flowmvi.core.dispatchers.CoroutineDispatchers
import com.hoc.flowmvi.data.remote.UserApiService
import com.hoc.flowmvi.data.remote.UserBody
import com.hoc.flowmvi.data.remote.UserResponse
import com.hoc.flowmvi.domain.entity.User
import com.hoc.flowmvi.domain.repository.UserError
+import com.hoc.flowmvi.test_utils.TestCoroutineDispatcherRule
+import com.hoc.flowmvi.test_utils.TestDispatchers
import io.mockk.clearAllMocks
import io.mockk.coEvery
import io.mockk.coVerify
@@ -19,17 +20,13 @@ import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import io.mockk.verifySequence
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.TestCoroutineDispatcher
-import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runBlockingTest
-import kotlinx.coroutines.test.setMain
+import org.junit.Rule
import java.io.IOException
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
@@ -95,16 +92,12 @@ private val USERS = listOf(
),
)
-@ExperimentalCoroutinesApi
-class TestDispatchersImpl(testDispatcher: TestCoroutineDispatcher) : CoroutineDispatchers {
- override val main: CoroutineDispatcher = testDispatcher
- override val io: CoroutineDispatcher = testDispatcher
-}
-
@ExperimentalCoroutinesApi
@ExperimentalTime
class UserRepositoryImplTest {
- private val testDispatcher = TestCoroutineDispatcher()
+ @get:Rule
+ val coroutineRule = TestCoroutineDispatcherRule()
+ private val testDispatcher get() = coroutineRule.testCoroutineDispatcher
private lateinit var repo: UserRepositoryImpl
private lateinit var userApiService: UserApiService
@@ -114,8 +107,6 @@ class UserRepositoryImplTest {
@BeforeTest
fun setup() {
- Dispatchers.setMain(testDispatcher)
-
userApiService = mockk()
responseToDomain = mockk()
domainToBody = mockk()
@@ -123,7 +114,7 @@ class UserRepositoryImplTest {
repo = UserRepositoryImpl(
userApiService = userApiService,
- dispatchers = TestDispatchersImpl(testDispatcher),
+ dispatchers = TestDispatchers(coroutineRule.testCoroutineDispatcher),
responseToDomain = responseToDomain,
domainToBody = domainToBody,
errorMapper = errorMapper
@@ -132,9 +123,6 @@ class UserRepositoryImplTest {
@AfterTest
fun tearDown() {
- testDispatcher.cleanupTestCoroutines()
- Dispatchers.resetMain()
-
confirmVerified(
userApiService,
responseToDomain,
diff --git a/domain/build.gradle.kts b/domain/build.gradle.kts
index 60b79fc4..a3d82c44 100644
--- a/domain/build.gradle.kts
+++ b/domain/build.gradle.kts
@@ -8,4 +8,5 @@ dependencies {
implementation(deps.arrow.core)
addUnitTest()
+ testImplementation(testUtils)
}
diff --git a/domain/src/test/java/com/hoc/flowmvi/domain/UseCaseTest.kt b/domain/src/test/java/com/hoc/flowmvi/domain/UseCaseTest.kt
index f396b5b4..d35c840e 100644
--- a/domain/src/test/java/com/hoc/flowmvi/domain/UseCaseTest.kt
+++ b/domain/src/test/java/com/hoc/flowmvi/domain/UseCaseTest.kt
@@ -10,6 +10,7 @@ import com.hoc.flowmvi.domain.usecase.GetUsersUseCase
import com.hoc.flowmvi.domain.usecase.RefreshGetUsersUseCase
import com.hoc.flowmvi.domain.usecase.RemoveUserUseCase
import com.hoc.flowmvi.domain.usecase.SearchUsersUseCase
+import com.hoc.flowmvi.test_utils.TestCoroutineDispatcherRule
import io.mockk.clearAllMocks
import io.mockk.coEvery
import io.mockk.coVerify
@@ -20,8 +21,8 @@ import io.mockk.verify
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.runBlockingTest
+import org.junit.Rule
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
import kotlin.test.Test
@@ -53,7 +54,9 @@ private val USERS = listOf(
@ExperimentalCoroutinesApi
class UseCaseTest {
- private val testDispatcher = TestCoroutineDispatcher()
+ @get:Rule
+ val coroutineRule = TestCoroutineDispatcherRule()
+ private val testDispatcher get() = coroutineRule.testCoroutineDispatcher
private lateinit var userRepository: UserRepository
private lateinit var getUsersUseCase: GetUsersUseCase
diff --git a/feature-add/build.gradle.kts b/feature-add/build.gradle.kts
index b55d001e..60e57890 100644
--- a/feature-add/build.gradle.kts
+++ b/feature-add/build.gradle.kts
@@ -17,7 +17,7 @@ android {
buildTypes {
release {
- isMinifyEnabled = true
+ isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
@@ -41,6 +41,7 @@ android {
dependencies {
implementation(domain)
implementation(core)
+ implementation(coreUi)
implementation(mviBase)
implementation(deps.androidx.appCompat)
diff --git a/feature-add/src/main/java/com/hoc/flowmvi/ui/add/AddActivity.kt b/feature-add/src/main/java/com/hoc/flowmvi/ui/add/AddActivity.kt
index e59f17b8..90b18744 100644
--- a/feature-add/src/main/java/com/hoc/flowmvi/ui/add/AddActivity.kt
+++ b/feature-add/src/main/java/com/hoc/flowmvi/ui/add/AddActivity.kt
@@ -6,11 +6,11 @@ import android.view.MenuItem
import androidx.core.view.isInvisible
import androidx.transition.AutoTransition
import androidx.transition.TransitionManager
-import com.hoc.flowmvi.core.clicks
-import com.hoc.flowmvi.core.firstChange
-import com.hoc.flowmvi.core.navigator.IntentProviders
-import com.hoc.flowmvi.core.textChanges
-import com.hoc.flowmvi.core.toast
+import com.hoc.flowmvi.core_ui.clicks
+import com.hoc.flowmvi.core_ui.firstChange
+import com.hoc.flowmvi.core_ui.navigator.IntentProviders
+import com.hoc.flowmvi.core_ui.textChanges
+import com.hoc.flowmvi.core_ui.toast
import com.hoc.flowmvi.mvi_base.AbstractMviActivity
import com.hoc.flowmvi.ui.add.databinding.ActivityAddBinding
import com.hoc081098.flowext.mapTo
diff --git a/feature-add/src/main/java/com/hoc/flowmvi/ui/add/AddModule.kt b/feature-add/src/main/java/com/hoc/flowmvi/ui/add/AddModule.kt
index 227df3f6..ca1f178d 100644
--- a/feature-add/src/main/java/com/hoc/flowmvi/ui/add/AddModule.kt
+++ b/feature-add/src/main/java/com/hoc/flowmvi/ui/add/AddModule.kt
@@ -1,6 +1,6 @@
package com.hoc.flowmvi.ui.add
-import com.hoc.flowmvi.core.navigator.IntentProviders
+import com.hoc.flowmvi.core_ui.navigator.IntentProviders
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
diff --git a/feature-main/build.gradle.kts b/feature-main/build.gradle.kts
index 2532aed4..50614bc0 100644
--- a/feature-main/build.gradle.kts
+++ b/feature-main/build.gradle.kts
@@ -17,7 +17,7 @@ android {
buildTypes {
release {
- isMinifyEnabled = true
+ isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
@@ -48,6 +48,7 @@ android {
dependencies {
implementation(domain)
implementation(core)
+ implementation(coreUi)
implementation(mviBase)
implementation(deps.androidx.appCompat)
diff --git a/feature-main/src/main/java/com/hoc/flowmvi/ui/main/MainActivity.kt b/feature-main/src/main/java/com/hoc/flowmvi/ui/main/MainActivity.kt
index f59257e9..18e99ffa 100644
--- a/feature-main/src/main/java/com/hoc/flowmvi/ui/main/MainActivity.kt
+++ b/feature-main/src/main/java/com/hoc/flowmvi/ui/main/MainActivity.kt
@@ -7,11 +7,11 @@ import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import com.hoc.flowmvi.core.SwipeLeftToDeleteCallback
-import com.hoc.flowmvi.core.clicks
-import com.hoc.flowmvi.core.navigator.Navigator
-import com.hoc.flowmvi.core.refreshes
-import com.hoc.flowmvi.core.toast
+import com.hoc.flowmvi.core_ui.SwipeLeftToDeleteCallback
+import com.hoc.flowmvi.core_ui.clicks
+import com.hoc.flowmvi.core_ui.navigator.Navigator
+import com.hoc.flowmvi.core_ui.refreshes
+import com.hoc.flowmvi.core_ui.toast
import com.hoc.flowmvi.domain.repository.UserError
import com.hoc.flowmvi.mvi_base.AbstractMviActivity
import com.hoc.flowmvi.ui.main.databinding.ActivityMainBinding
diff --git a/feature-search/build.gradle.kts b/feature-search/build.gradle.kts
index 0b0b5c7e..fdd552b4 100644
--- a/feature-search/build.gradle.kts
+++ b/feature-search/build.gradle.kts
@@ -18,7 +18,7 @@ android {
buildTypes {
release {
- isMinifyEnabled = true
+ isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
@@ -42,6 +42,7 @@ android {
dependencies {
implementation(domain)
implementation(core)
+ implementation(coreUi)
implementation(mviBase)
implementation(deps.androidx.appCompat)
diff --git a/feature-search/src/main/java/com/hoc/flowmvi/ui/search/SearchActivity.kt b/feature-search/src/main/java/com/hoc/flowmvi/ui/search/SearchActivity.kt
index e0908242..8524cd48 100644
--- a/feature-search/src/main/java/com/hoc/flowmvi/ui/search/SearchActivity.kt
+++ b/feature-search/src/main/java/com/hoc/flowmvi/ui/search/SearchActivity.kt
@@ -10,11 +10,11 @@ import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
-import com.hoc.flowmvi.core.SearchViewQueryTextEvent
-import com.hoc.flowmvi.core.clicks
-import com.hoc.flowmvi.core.navigator.IntentProviders
-import com.hoc.flowmvi.core.queryTextEvents
-import com.hoc.flowmvi.core.toast
+import com.hoc.flowmvi.core_ui.SearchViewQueryTextEvent
+import com.hoc.flowmvi.core_ui.clicks
+import com.hoc.flowmvi.core_ui.navigator.IntentProviders
+import com.hoc.flowmvi.core_ui.queryTextEvents
+import com.hoc.flowmvi.core_ui.toast
import com.hoc.flowmvi.domain.repository.UserError
import com.hoc.flowmvi.mvi_base.AbstractMviActivity
import com.hoc.flowmvi.ui.search.databinding.ActivitySearchBinding
diff --git a/feature-search/src/main/java/com/hoc/flowmvi/ui/search/SearchModule.kt b/feature-search/src/main/java/com/hoc/flowmvi/ui/search/SearchModule.kt
index 38b1c6d7..64895147 100644
--- a/feature-search/src/main/java/com/hoc/flowmvi/ui/search/SearchModule.kt
+++ b/feature-search/src/main/java/com/hoc/flowmvi/ui/search/SearchModule.kt
@@ -1,6 +1,6 @@
package com.hoc.flowmvi.ui.search
-import com.hoc.flowmvi.core.navigator.IntentProviders
+import com.hoc.flowmvi.core_ui.navigator.IntentProviders
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
import org.koin.androidx.viewmodel.dsl.viewModel
diff --git a/mvi/mvi-base/build.gradle.kts b/mvi/mvi-base/build.gradle.kts
index 8773f946..220af562 100644
--- a/mvi/mvi-base/build.gradle.kts
+++ b/mvi/mvi-base/build.gradle.kts
@@ -37,7 +37,7 @@ dependencies {
implementation(deps.lifecycle.runtimeKtx)
implementation(deps.coroutines.core)
- implementation(core)
+ implementation(coreUi)
implementation(deps.timber)
addUnitTest()
diff --git a/mvi/mvi-base/src/main/java/com/hoc/flowmvi/mvi_base/AbstractMviActivity.kt b/mvi/mvi-base/src/main/java/com/hoc/flowmvi/mvi_base/AbstractMviActivity.kt
index cbb29495..3ccbe35a 100644
--- a/mvi/mvi-base/src/main/java/com/hoc/flowmvi/mvi_base/AbstractMviActivity.kt
+++ b/mvi/mvi-base/src/main/java/com/hoc/flowmvi/mvi_base/AbstractMviActivity.kt
@@ -5,7 +5,7 @@ import androidx.annotation.CallSuper
import androidx.annotation.LayoutRes
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
-import com.hoc.flowmvi.core.collectIn
+import com.hoc.flowmvi.core_ui.collectIn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
diff --git a/mvi/mvi-testing/build.gradle.kts b/mvi/mvi-testing/build.gradle.kts
index 97b4be58..1c467baf 100644
--- a/mvi/mvi-testing/build.gradle.kts
+++ b/mvi/mvi-testing/build.gradle.kts
@@ -16,12 +16,28 @@ android {
}
buildTypes {
+ debug {
+ (!isCiBuild).let {
+ buildConfigField(
+ type = it::class.java.simpleName,
+ name = "ENABLE_LOG_TEST",
+ value = it.toString(),
+ )
+ }
+ }
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
+ false.let {
+ buildConfigField(
+ type = it::class.java.simpleName,
+ name = "ENABLE_LOG_TEST",
+ value = it.toString(),
+ )
+ }
}
}
compileOptions {
@@ -36,6 +52,7 @@ dependencies {
implementation(deps.coroutines.core)
implementation(mviBase)
+ implementation(testUtils)
implementation(deps.timber)
implementation(deps.arrow.core)
diff --git a/mvi/mvi-testing/src/main/java/com/flowmvi/mvi_testing/BaseMviViewModelTest.kt b/mvi/mvi-testing/src/main/java/com/flowmvi/mvi_testing/BaseMviViewModelTest.kt
index fd62354a..4dbb5fa1 100644
--- a/mvi/mvi-testing/src/main/java/com/flowmvi/mvi_testing/BaseMviViewModelTest.kt
+++ b/mvi/mvi-testing/src/main/java/com/flowmvi/mvi_testing/BaseMviViewModelTest.kt
@@ -7,19 +7,18 @@ import com.hoc.flowmvi.mvi_base.MviIntent
import com.hoc.flowmvi.mvi_base.MviSingleEvent
import com.hoc.flowmvi.mvi_base.MviViewModel
import com.hoc.flowmvi.mvi_base.MviViewState
+import com.hoc.flowmvi.test_utils.TestCoroutineDispatcherRule
import io.mockk.clearAllMocks
import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.TestCoroutineDispatcher
-import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runBlockingTest
-import kotlinx.coroutines.test.setMain
+import org.junit.Rule
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
import kotlin.test.assertEquals
@@ -34,12 +33,20 @@ abstract class BaseMviViewModelTest<
E : MviSingleEvent,
VM : MviViewModel,
> {
- private val testDispatcher = TestCoroutineDispatcher()
+ @get:Rule
+ val coroutineRule = TestCoroutineDispatcherRule()
+
+ protected val testDispatcher get() = coroutineRule.testCoroutineDispatcher
@CallSuper
@BeforeTest
open fun setup() {
- Dispatchers.setMain(testDispatcher)
+ }
+
+ @CallSuper
+ @AfterTest
+ open fun tearDown() {
+ clearAllMocks()
}
protected fun test(
@@ -48,12 +55,21 @@ abstract class BaseMviViewModelTest<
expectedStates: List Unit, S>>,
expectedEvents: List Unit, E>>,
delayAfterDispatchingIntents: Duration = Duration.ZERO,
- logging: Boolean = true,
+ logging: Boolean = BuildConfig.ENABLE_LOG_TEST,
intentsBeforeCollecting: Flow? = null,
otherAssertions: (suspend () -> Unit)? = null,
) = testDispatcher.runBlockingTest {
+ fun logIfEnabled(s: () -> String) = if (logging) println(s()) else Unit
+
val vm = vmProducer()
- intentsBeforeCollecting?.collect { vm.processIntent(it) }
+ intentsBeforeCollecting
+ ?.onCompletion { logIfEnabled { "---------------" } }
+ ?.collect {
+ vm.processIntent(it)
+ logIfEnabled { "[BEFORE] Dispatch $it -> $vm" }
+ }
+
+ logIfEnabled { "[START] $vm" }
val states = mutableListOf()
val events = mutableListOf()
@@ -61,13 +77,15 @@ abstract class BaseMviViewModelTest<
val stateJob = launch(start = CoroutineStart.UNDISPATCHED) { vm.viewState.toList(states) }
val eventJob = launch(start = CoroutineStart.UNDISPATCHED) { vm.singleEvent.toList(events) }
- intents.collect { vm.processIntent(it) }
+ intents.collect {
+ vm.processIntent(it)
+ logIfEnabled { "[DISPATCH] Dispatch $it -> $vm" }
+ }
delay(delayAfterDispatchingIntents)
+ logIfEnabled { "---------------" }
- if (logging) {
- println(states)
- println(events)
- }
+ logIfEnabled { "[DONE] states=${states.joinToStringWithIndex()}" }
+ logIfEnabled { "[DONE] events=${events.joinToStringWithIndex()}" }
assertEquals(expectedStates.size, states.size, "States size")
expectedStates.withIndex().zip(states).forEach { (indexedValue, state) ->
@@ -103,14 +121,16 @@ abstract class BaseMviViewModelTest<
stateJob.cancel()
eventJob.cancel()
}
-
- @CallSuper
- @AfterTest
- open fun tearDown() {
- Dispatchers.resetMain()
- testDispatcher.cleanupTestCoroutines()
- clearAllMocks()
- }
}
fun Iterable.mapRight(): List Unit, T>> = map { it.right() }
+
+private fun List.joinToStringWithIndex(): String {
+ return withIndex().joinToString(
+ separator = ",\n",
+ prefix = "[\n",
+ postfix = "]",
+ ) { (i, v) ->
+ " [$i]: $v"
+ }
+}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index b7dfdfed..cf8a3884 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -7,6 +7,8 @@ include(":feature-search")
include(":domain")
include(":data")
include(":core")
+include(":core-ui")
+include(":test-utils")
includeProject(":mvi-base", "mvi/mvi-base")
includeProject(":mvi-testing", "mvi/mvi-testing")
diff --git a/test-utils/.gitignore b/test-utils/.gitignore
new file mode 100644
index 00000000..796b96d1
--- /dev/null
+++ b/test-utils/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/test-utils/build.gradle.kts b/test-utils/build.gradle.kts
new file mode 100644
index 00000000..bef2a2c2
--- /dev/null
+++ b/test-utils/build.gradle.kts
@@ -0,0 +1,10 @@
+plugins {
+ kotlin
+}
+
+dependencies {
+ implementation(deps.coroutines.core)
+ implementation(core)
+
+ addUnitTest(testImplementation = false)
+}
diff --git a/test-utils/src/main/java/com/hoc/flowmvi/test_utils/TestCoroutineDispatcherRule.kt b/test-utils/src/main/java/com/hoc/flowmvi/test_utils/TestCoroutineDispatcherRule.kt
new file mode 100644
index 00000000..1b25e277
--- /dev/null
+++ b/test-utils/src/main/java/com/hoc/flowmvi/test_utils/TestCoroutineDispatcherRule.kt
@@ -0,0 +1,22 @@
+package com.hoc.flowmvi.test_utils
+
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestCoroutineDispatcher
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.setMain
+import org.junit.rules.TestWatcher
+import org.junit.runner.Description
+
+@ExperimentalCoroutinesApi
+class TestCoroutineDispatcherRule(val testCoroutineDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()) :
+ TestWatcher() {
+ override fun starting(description: Description) {
+ Dispatchers.setMain(testCoroutineDispatcher)
+ }
+
+ override fun finished(description: Description) {
+ Dispatchers.resetMain()
+ testCoroutineDispatcher.cleanupTestCoroutines()
+ }
+}
diff --git a/test-utils/src/main/java/com/hoc/flowmvi/test_utils/TestDispatchers.kt b/test-utils/src/main/java/com/hoc/flowmvi/test_utils/TestDispatchers.kt
new file mode 100644
index 00000000..5faa6744
--- /dev/null
+++ b/test-utils/src/main/java/com/hoc/flowmvi/test_utils/TestDispatchers.kt
@@ -0,0 +1,13 @@
+package com.hoc.flowmvi.test_utils
+
+import com.hoc.flowmvi.core.dispatchers.CoroutineDispatchers
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestCoroutineDispatcher
+
+@ExperimentalCoroutinesApi
+class TestDispatchers(testCoroutineDispatcher: TestCoroutineDispatcher) :
+ CoroutineDispatchers {
+ override val main: CoroutineDispatcher = testCoroutineDispatcher
+ override val io: CoroutineDispatcher = testCoroutineDispatcher
+}