diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2b4b0cf..6362246 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,3 +26,6 @@ jobs: coverage: uses: ./.github/workflows/coverage.yml + + code-style: + uses: ./.github/workflows/code-style.yml \ No newline at end of file diff --git a/.github/workflows/code-style.yml b/.github/workflows/code-style.yml new file mode 100644 index 0000000..7ad3489 --- /dev/null +++ b/.github/workflows/code-style.yml @@ -0,0 +1,24 @@ +on: + workflow_call: + +jobs: + code-style: + name: 👔 Code style + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-jdk-gradle + + - name: 🕵🏻‍♂️ Detekt + run: | + ./gradlew --no-daemon --parallel --console=plain \ + detekt + echo "### Detekt Report" >> $GITHUB_STEP_SUMMARY + if [ -f build/reports/detekt/detekt.txt ]; then + echo '```' >> $GITHUB_STEP_SUMMARY + cat build/reports/detekt/detekt.txt >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + else + echo "No report found." >> $GITHUB_STEP_SUMMARY + fi \ No newline at end of file diff --git a/.idea/detekt.xml b/.idea/detekt.xml new file mode 100644 index 0000000..03d0954 --- /dev/null +++ b/.idea/detekt.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/android-test-util/build.gradle.kts b/android-test-util/build.gradle.kts index f16e786..da3fffe 100644 --- a/android-test-util/build.gradle.kts +++ b/android-test-util/build.gradle.kts @@ -20,7 +20,6 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - plugins { alias(libs.plugins.android.library) alias(libs.plugins.jetbrains.kotlin.android) diff --git a/android-test-util/src/main/kotlin/net/opatry/test/util/android/KoinTestRule.kt b/android-test-util/src/main/kotlin/net/opatry/test/util/android/KoinTestRule.kt index 0e3868e..851b6c3 100644 --- a/android-test-util/src/main/kotlin/net/opatry/test/util/android/KoinTestRule.kt +++ b/android-test-util/src/main/kotlin/net/opatry/test/util/android/KoinTestRule.kt @@ -49,4 +49,4 @@ class KoinTestRule( override fun finished(description: Description) { unloadKoinModules(modules) } -} \ No newline at end of file +} diff --git a/android-test-util/src/main/kotlin/net/opatry/test/util/android/navigation/NavHostControllerTestExt.kt b/android-test-util/src/main/kotlin/net/opatry/test/util/android/navigation/NavHostControllerTestExt.kt index 9e17d88..72e2cad 100644 --- a/android-test-util/src/main/kotlin/net/opatry/test/util/android/navigation/NavHostControllerTestExt.kt +++ b/android-test-util/src/main/kotlin/net/opatry/test/util/android/navigation/NavHostControllerTestExt.kt @@ -9,4 +9,4 @@ inline fun NavHostController.assertRoute() { assertThat(currentDestination?.hasRoute()) .withFailMessage("Expected %s route but current route is %s", T::class.java.name, currentDestination?.route) .isTrue() -} \ No newline at end of file +} diff --git a/build.gradle.kts b/build.gradle.kts index 7f2c30d..5252fbc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -20,6 +20,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +import io.gitlab.arturbosch.detekt.Detekt import kotlinx.kover.gradle.plugin.dsl.CoverageUnit import org.gradle.api.tasks.testing.logging.TestExceptionFormat @@ -34,6 +35,7 @@ plugins { alias(libs.plugins.android.library) apply false alias(libs.plugins.about.libraries) apply false alias(libs.plugins.kover) + alias(libs.plugins.detekt) } val koverProjects = listOf( @@ -100,6 +102,33 @@ kover { } } +allprojects { + project.afterEvaluate { + apply(plugin = libs.plugins.detekt.get().pluginId) + detekt { + config.setFrom("$rootDir/detekt.yml") + buildUponDefaultConfig = true + allRules = true + parallel = true + ignoreFailures = false + } + + dependencies { + detektPlugins(projects.detektRules) + } + } + + tasks.withType { dependsOn(":detekt-rules:assemble") } + tasks.withType().configureEach { + reports { + txt.required.set(true) + html.required.set(false) + xml.required.set(false) + sarif.required.set(false) + } + } +} + subprojects { tasks { findByName("test") ?: return@tasks diff --git a/detekt-rules/build.gradle.kts b/detekt-rules/build.gradle.kts new file mode 100644 index 0000000..2e1909f --- /dev/null +++ b/detekt-rules/build.gradle.kts @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 Olivier Patry + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software + * is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +plugins { + kotlin("jvm") +} + +dependencies { + compileOnly(libs.detekt.api) + testImplementation(libs.detekt.test) + testImplementation(libs.junit) + testImplementation(libs.assertj.core) +} \ No newline at end of file diff --git a/detekt-rules/src/main/kotlin/net/opatry/detekt/CleanArchitectureBoundaryRule.kt b/detekt-rules/src/main/kotlin/net/opatry/detekt/CleanArchitectureBoundaryRule.kt new file mode 100644 index 0000000..a1ac3af --- /dev/null +++ b/detekt-rules/src/main/kotlin/net/opatry/detekt/CleanArchitectureBoundaryRule.kt @@ -0,0 +1,61 @@ +package net.opatry.detekt + +import io.gitlab.arturbosch.detekt.api.CodeSmell +import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.Debt +import io.gitlab.arturbosch.detekt.api.Entity +import io.gitlab.arturbosch.detekt.api.Issue +import io.gitlab.arturbosch.detekt.api.Rule +import io.gitlab.arturbosch.detekt.api.Severity +import org.jetbrains.kotlin.psi.KtImportDirective + +class CleanArchitectureBoundaryRule(config: Config = Config.empty) : Rule(config) { + private companion object { + private val violations = mapOf( + ".domain." to listOf(".presentation.", ".data.", ".ui."), + ".presentation." to listOf(".data.", ".ui."), + ".ui." to listOf(".domain.", ".data."), + ".navigation." to listOf(".domain.", ".data."), + ) + } + + override val issue = Issue( + id = "CleanArchitectureBoundaryRule", + severity = Severity.CodeSmell, + description = "Clean Architecture boundary violation detected", + debt = Debt.TWENTY_MINS, + ) + + override fun visitImportDirective(importDirective: KtImportDirective) { + super.visitImportDirective(importDirective) + + val importPath = importDirective.importPath?.pathStr ?: return + val currentFile = importDirective.containingKtFile + val currentPackage = currentFile.packageFqName.asString() + + checkArchitectureBoundaries(currentPackage, importPath, importDirective) + } + + private fun checkArchitectureBoundaries( + currentPackage: String, + importPath: String, + importDirective: KtImportDirective, + ) { + violations + .filterKeys { "$currentPackage.".contains(it) } + .forEach { (fromLayer, toLayers) -> + toLayers + .filter { importPath.contains(it) } + .forEach { toLayer -> + val message = "Layer ${fromLayer.trim('.')} should not import from ${toLayer.trim('.')} layer" + report( + CodeSmell( + issue = issue, + entity = Entity.from(importDirective), + message = message, + ) + ) + } + } + } +} diff --git a/detekt-rules/src/main/kotlin/net/opatry/detekt/CustomRuleSetProvider.kt b/detekt-rules/src/main/kotlin/net/opatry/detekt/CustomRuleSetProvider.kt new file mode 100644 index 0000000..fb1ca4c --- /dev/null +++ b/detekt-rules/src/main/kotlin/net/opatry/detekt/CustomRuleSetProvider.kt @@ -0,0 +1,16 @@ +package net.opatry.detekt + +import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.RuleSet +import io.gitlab.arturbosch.detekt.api.RuleSetProvider + +class CustomRuleSetProvider : RuleSetProvider { + override val ruleSetId: String = "h2go-rules" + + override fun instance(config: Config) = RuleSet( + ruleSetId, + listOf( + CleanArchitectureBoundaryRule(config) + ) + ) +} diff --git a/detekt-rules/src/main/resources/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider b/detekt-rules/src/main/resources/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider new file mode 100644 index 0000000..0edd05d --- /dev/null +++ b/detekt-rules/src/main/resources/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider @@ -0,0 +1 @@ +net.opatry.detekt.CustomRuleSetProvider \ No newline at end of file diff --git a/detekt-rules/src/test/java/net/opatry/detetk/CleanArchitectureBoundaryRuleTest.kt b/detekt-rules/src/test/java/net/opatry/detetk/CleanArchitectureBoundaryRuleTest.kt new file mode 100644 index 0000000..cc13b67 --- /dev/null +++ b/detekt-rules/src/test/java/net/opatry/detetk/CleanArchitectureBoundaryRuleTest.kt @@ -0,0 +1,344 @@ +package net.opatry.detetk + +import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.Finding +import io.gitlab.arturbosch.detekt.rules.KotlinCoreEnvironmentTest +import io.gitlab.arturbosch.detekt.test.compileAndLint +import net.opatry.detekt.CleanArchitectureBoundaryRule +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +@KotlinCoreEnvironmentTest +class CleanArchitectureBoundaryRuleTest { + + private val rule = CleanArchitectureBoundaryRule(Config.empty) + + @Test + fun `should detect domain layer importing presentation layer`() { + val code = """ + package com.example.domain.usecase + + import com.example.presentation.ViewModel + import com.example.domain.model.User + + class GetUserUseCase + """.trimIndent() + + val findings = rule.compileAndLint(code) + + assertThat(findings.map(Finding::message)).containsExactlyInAnyOrder( + "Layer domain should not import from presentation layer", + ) + } + + @Test + fun `should detect domain layer importing data layer`() { + val code = """ + package com.example.domain.repository + + import com.example.data.database.UserDao + import com.example.domain.model.User + + interface UserRepository + """.trimIndent() + + val findings = rule.compileAndLint(code) + + assertThat(findings.map(Finding::message)).containsExactlyInAnyOrder( + "Layer domain should not import from data layer", + ) + } + + @Test + fun `should detect domain layer importing ui layer`() { + val code = """ + package com.example.domain.usecase + + import com.example.ui.components.Button + import com.example.domain.model.User + + class GetUserUseCase + """.trimIndent() + + val findings = rule.compileAndLint(code) + + assertThat(findings.map(Finding::message)).containsExactlyInAnyOrder( + "Layer domain should not import from ui layer", + ) + } + + @Test + fun `should detect presentation layer importing data layer`() { + val code = """ + package com.example.presentation.viewmodel + + import com.example.data.repository.UserRepositoryImpl + import com.example.domain.usecase.GetUserUseCase + + class UserViewModel( + private val getUserUseCase: GetUserUseCase + ) + """.trimIndent() + + val findings = rule.compileAndLint(code) + + assertThat(findings.map(Finding::message)).containsExactlyInAnyOrder( + "Layer presentation should not import from data layer", + ) + } + + @Test + fun `should detect presentation layer importing ui layer`() { + val code = """ + package com.example.presentation.mapper + + import com.example.ui.model.UserUiModel + import com.example.domain.model.User + + class UserMapper { + fun mapToUi(user: User): UserUiModel = UserUiModel() + } + """.trimIndent() + + val findings = rule.compileAndLint(code) + + assertThat(findings.map(Finding::message)).containsExactlyInAnyOrder( + "Layer presentation should not import from ui layer", + ) + } + + @Test + fun `should detect ui layer importing domain layer`() { + val code = """ + package com.example.ui.screen + + import com.example.domain.model.User + import com.example.presentation.viewmodel.UserViewModel + + class UserScreen( + private val viewModel: UserViewModel + ) + """.trimIndent() + + val findings = rule.compileAndLint(code) + + assertThat(findings.map(Finding::message)).containsExactlyInAnyOrder( + "Layer ui should not import from domain layer", + ) + } + + @Test + fun `should detect ui layer importing data layer`() { + val code = """ + package com.example.ui.components + + import com.example.data.database.UserDao + import com.example.presentation.viewmodel.UserViewModel + + class UserComponent + """.trimIndent() + + val findings = rule.compileAndLint(code) + + assertThat(findings.map(Finding::message)).containsExactlyInAnyOrder( + "Layer ui should not import from data layer", + ) + } + + @Test + fun `should detect multiple violations in single file`() { + val code = """ + package com.example.domain.usecase + + import com.example.presentation.ViewModel + import com.example.data.repository.UserRepository + import com.example.ui.model.UserUiModel + import com.example.domain.model.User + + class GetUserUseCase + """.trimIndent() + + val findings = rule.compileAndLint(code) + + assertThat(findings.map(Finding::message)).containsExactlyInAnyOrder( + "Layer domain should not import from presentation layer", + "Layer domain should not import from data layer", + "Layer domain should not import from ui layer", + ) + } + + @Test + fun `should detect violation when package name ends with forbidden package pattern`() { + val code = """ + package com.example.domain + + import com.example.ui.UserUiModel + + class GetUserUseCase + """.trimIndent() + + val findings = rule.compileAndLint(code) + + assertThat(findings.map(Finding::message)).containsExactlyInAnyOrder( + "Layer domain should not import from ui layer", + ) + } + + @Test + fun `should allow valid domain layer imports`() { + val code = """ + package com.example.domain.usecase + + import com.example.domain.model.User + import com.example.domain.repository.UserRepository + import java.util.Date + import kotlin.collections.List + + class GetUserUseCase + """.trimIndent() + + val findings = rule.compileAndLint(code) + + assertThat(findings).isEmpty() + } + + @Test + fun `should allow valid presentation layer imports`() { + val code = """ + package com.example.presentation.viewmodel + + import com.example.domain.usecase.GetUserUseCase + import com.example.domain.model.User + import com.example.presentation.mapper.UserMapper + import kotlinx.coroutines.flow.Flow + + class UserViewModel + """.trimIndent() + + val findings = rule.compileAndLint(code) + + assertThat(findings).isEmpty() + } + + @Test + fun `should allow valid ui layer imports`() { + val code = """ + package com.example.ui.screen + + import com.example.presentation.viewmodel.UserViewModel + import com.example.ui.components.Button + import androidx.compose.runtime.Composable + import androidx.compose.material3.Text + + @Composable + fun UserScreen( + viewModel: UserViewModel + ) { + Text("Hello") + } + """.trimIndent() + + val findings = rule.compileAndLint(code) + + assertThat(findings).isEmpty() + } + + @Test + fun `should allow valid data layer imports`() { + val code = """ + package com.example.data.repository + + import com.example.domain.model.User + import com.example.domain.repository.UserRepository + import com.example.data.database.UserDao + import com.example.data.network.UserApi + + class UserRepositoryImpl( + private val userDao: UserDao, + private val userApi: UserApi + ) : UserRepository { + override fun getUser(): User = User() + } + """.trimIndent() + + val findings = rule.compileAndLint(code) + + assertThat(findings).isEmpty() + } + + @Test + fun `should handle nested package structures`() { + val code = """ + package com.example.feature.user.domain.usecase + + import com.example.feature.user.presentation.viewmodel.UserViewModel + import com.example.feature.user.domain.model.User + + class GetUserUseCase + """.trimIndent() + + val findings = rule.compileAndLint(code) + + assertThat(findings.map(Finding::message)).containsExactlyInAnyOrder( + "Layer domain should not import from presentation layer", + ) + } + + @Test + fun `should handle different module structures`() { + val code = """ + package com.example.moduleA.domain.service + + import com.example.moduleB.data.repository.UserRepository + import com.example.moduleA.domain.model.User + + class UserService + """.trimIndent() + + val findings = rule.compileAndLint(code) + + assertThat(findings.map(Finding::message)).containsExactlyInAnyOrder( + "Layer domain should not import from data layer", + ) + } + + @Test + fun `should ignore non-architecture related imports`() { + val code = """ + package com.example.domain.usecase + + import java.util.UUID + import kotlin.collections.List + import kotlinx.coroutines.flow.Flow + import javax.inject.Inject + import com.google.gson.Gson + import com.example.domain.model.User + + class GetUserUseCase @Inject constructor() { + fun execute(): Flow> = TODO() + } + """.trimIndent() + + val findings = rule.compileAndLint(code) + + assertThat(findings).isEmpty() + } + + @Test + fun `should handle star imports`() { + val code = """ + package com.example.domain.usecase + + import com.example.presentation.viewmodel.* + import com.example.domain.model.User + + class GetUserUseCase + """.trimIndent() + + val findings = rule.compileAndLint(code) + + assertThat(findings.map(Finding::message)).containsExactlyInAnyOrder( + "Layer domain should not import from presentation layer", + ) + } +} diff --git a/detekt.yml b/detekt.yml new file mode 100644 index 0000000..5bb0a3a --- /dev/null +++ b/detekt.yml @@ -0,0 +1,39 @@ +processors: + active: true + +naming: + FunctionNaming: + active: true + ignoreAnnotated: ['Composable'] + functionPattern: '^[a-z][A-Za-z0-9]*$' + MatchingDeclarationName: + active: false + excludes: ['**/ui/**'] + ignoreAnnotated: ['Composable', 'VisibleForTesting'] + +style: + UnusedPrivateMember: + active: true + ignoreAnnotated: ['Preview', 'PreviewLightDark'] + MaxLineLength: + active: true + maxLineLength: 120 + excludePackageStatements: true + excludeImportStatements: true + excludeCommentStatements: true + ignoreAnnotated: ['Test'] + +complexity: + LongMethod: + active: true + ignoreAnnotated: ['Composable', 'Test'] + threshold: 60 + +performance: + SpreadOperator: + active: false + +h2go-rules: + active: true + CleanArchitectureBoundaryRule: + active: true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0f079c6..a591f83 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,6 +23,7 @@ junit4 = "4.13.2" junit = "5.10.2" assertj = "3.27.3" kover = "0.9.1" +detekt = "1.23.8" androidx-test-runner = "1.6.2" [libraries] @@ -80,6 +81,9 @@ junit4 = { group = "junit", name = "junit", version.ref = "junit4" } junit = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" } assertj-core = { module = "org.assertj:assertj-core", version.ref = "assertj" } +detekt-api = { module = "io.gitlab.arturbosch.detekt:detekt-api", version.ref = "detekt" } +detekt-test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version.ref = "detekt" } + [bundles] mockito = [ "mockito-core", @@ -98,3 +102,4 @@ ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } androidx-room = { id = "androidx.room", version.ref = "room" } about-libraries = { id = "com.mikepenz.aboutlibraries.plugin", version.ref = "about-libraries" } kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" } +detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } diff --git a/h2go-app/build.gradle.kts b/h2go-app/build.gradle.kts index a704880..ea0b938 100644 --- a/h2go-app/build.gradle.kts +++ b/h2go-app/build.gradle.kts @@ -87,11 +87,11 @@ android { kotlin { jvmToolchain(17) } - + buildFeatures { compose = true } - + packaging { resources { excludes += "/META-INF/{AL2.0,LGPL2.1}" diff --git a/h2go-app/src/androidTest/java/net/opatry/h2go/app/navigation/MainNavigationTest.kt b/h2go-app/src/androidTest/java/net/opatry/h2go/app/navigation/MainNavigationTest.kt index 05bb512..0726647 100644 --- a/h2go-app/src/androidTest/java/net/opatry/h2go/app/navigation/MainNavigationTest.kt +++ b/h2go-app/src/androidTest/java/net/opatry/h2go/app/navigation/MainNavigationTest.kt @@ -122,4 +122,4 @@ class MainNavigationTest { // Then navController.assertRoute() } -} \ No newline at end of file +} diff --git a/h2go-app/src/androidTest/java/net/opatry/h2go/data/di/databaseTestModule.kt b/h2go-app/src/androidTest/java/net/opatry/h2go/data/di/databaseTestModule.kt index 7d4ad50..f4a12e3 100644 --- a/h2go-app/src/androidTest/java/net/opatry/h2go/data/di/databaseTestModule.kt +++ b/h2go-app/src/androidTest/java/net/opatry/h2go/data/di/databaseTestModule.kt @@ -35,4 +35,4 @@ val databaseTestModule = module { single { get().userPreferencesDao() } -} \ No newline at end of file +} diff --git a/h2go-app/src/main/java/net/opatry/h2go/app/H2GoApplication.kt b/h2go-app/src/main/java/net/opatry/h2go/app/H2GoApplication.kt index 9a66516..f2c5ce4 100644 --- a/h2go-app/src/main/java/net/opatry/h2go/app/H2GoApplication.kt +++ b/h2go-app/src/main/java/net/opatry/h2go/app/H2GoApplication.kt @@ -42,4 +42,4 @@ class H2GoApplication : Application(), KoinStartup { onboardingModule, ) } -} \ No newline at end of file +} diff --git a/h2go-app/src/main/java/net/opatry/h2go/app/data/H2GoDatabase.kt b/h2go-app/src/main/java/net/opatry/h2go/app/data/H2GoDatabase.kt index fbf4400..b004e22 100644 --- a/h2go-app/src/main/java/net/opatry/h2go/app/data/H2GoDatabase.kt +++ b/h2go-app/src/main/java/net/opatry/h2go/app/data/H2GoDatabase.kt @@ -35,4 +35,4 @@ import net.opatry.h2go.preference.data.entity.UserPreferencesEntity ) abstract class H2GoDatabase : RoomDatabase() { abstract fun userPreferencesDao(): UserPreferencesDao -} \ No newline at end of file +} diff --git a/h2go-app/src/main/java/net/opatry/h2go/app/data/di/databaseModule.kt b/h2go-app/src/main/java/net/opatry/h2go/app/data/di/databaseModule.kt index 36509a4..8b22ce7 100644 --- a/h2go-app/src/main/java/net/opatry/h2go/app/data/di/databaseModule.kt +++ b/h2go-app/src/main/java/net/opatry/h2go/app/data/di/databaseModule.kt @@ -33,4 +33,4 @@ val databaseModule = module { } single { get().userPreferencesDao() } -} \ No newline at end of file +} diff --git a/h2go-app/src/main/java/net/opatry/h2go/app/navigation/MainNavigation.kt b/h2go-app/src/main/java/net/opatry/h2go/app/navigation/MainNavigation.kt index bd73003..351ee51 100644 --- a/h2go-app/src/main/java/net/opatry/h2go/app/navigation/MainNavigation.kt +++ b/h2go-app/src/main/java/net/opatry/h2go/app/navigation/MainNavigation.kt @@ -49,4 +49,4 @@ fun MainNavigation(navController: NavHostController = rememberNavController()) { } } } -} \ No newline at end of file +} diff --git a/h2go-app/src/main/java/net/opatry/h2go/app/navigation/MainRoutes.kt b/h2go-app/src/main/java/net/opatry/h2go/app/navigation/MainRoutes.kt index bd5207f..db4be9a 100644 --- a/h2go-app/src/main/java/net/opatry/h2go/app/navigation/MainRoutes.kt +++ b/h2go-app/src/main/java/net/opatry/h2go/app/navigation/MainRoutes.kt @@ -27,4 +27,4 @@ import kotlinx.serialization.Serializable object MainRoutes { @Serializable data object Main -} \ No newline at end of file +} diff --git a/h2go-app/src/test/java/net/opatry/h2go/app/di/H2GoDITest.kt b/h2go-app/src/test/java/net/opatry/h2go/app/di/H2GoDITest.kt index 408676f..fe07a6b 100644 --- a/h2go-app/src/test/java/net/opatry/h2go/app/di/H2GoDITest.kt +++ b/h2go-app/src/test/java/net/opatry/h2go/app/di/H2GoDITest.kt @@ -44,4 +44,4 @@ class H2GoDITest { } allModules.verify() } -} \ No newline at end of file +} diff --git a/h2go-app/src/test/java/net/opatry/h2go/data/di/DatabaseModuleTest.kt b/h2go-app/src/test/java/net/opatry/h2go/data/di/DatabaseModuleTest.kt index f080d75..fe0f577 100644 --- a/h2go-app/src/test/java/net/opatry/h2go/data/di/DatabaseModuleTest.kt +++ b/h2go-app/src/test/java/net/opatry/h2go/data/di/DatabaseModuleTest.kt @@ -34,4 +34,4 @@ class DatabaseModuleTest { fun `verify database module`() { databaseModule.verify() } -} \ No newline at end of file +} diff --git a/onboarding/build.gradle.kts b/onboarding/build.gradle.kts index b2bccaf..c2cae06 100644 --- a/onboarding/build.gradle.kts +++ b/onboarding/build.gradle.kts @@ -69,4 +69,4 @@ dependencies { testImplementation(libs.assertj.core) testImplementation(libs.bundles.mockito) testImplementation(libs.koin.test) -} \ No newline at end of file +} diff --git a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/di/onboardingModule.kt b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/di/onboardingModule.kt index d55bef0..e7de291 100644 --- a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/di/onboardingModule.kt +++ b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/di/onboardingModule.kt @@ -38,4 +38,4 @@ val onboardingModule = module { singleOf(::SaveInitialUserPreferencesUseCase) singleOf(::UserVolumeUnitMapper) viewModelOf(::PreferencesViewModel) -} \ No newline at end of file +} diff --git a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/domain/CheckUserPreferencesExistUseCase.kt b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/domain/CheckUserPreferencesExistUseCase.kt index 5806b7b..e99e8c5 100644 --- a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/domain/CheckUserPreferencesExistUseCase.kt +++ b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/domain/CheckUserPreferencesExistUseCase.kt @@ -29,4 +29,4 @@ class CheckUserPreferencesExistUseCase( private val userPreferencesRepository: UserPreferencesRepository, ) { suspend operator fun invoke(): Boolean = userPreferencesRepository.getUserPreferences().firstOrNull() != null -} \ No newline at end of file +} diff --git a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/domain/SaveInitialUserPreferencesUseCase.kt b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/domain/SaveInitialUserPreferencesUseCase.kt index d5194f7..bc0270a 100644 --- a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/domain/SaveInitialUserPreferencesUseCase.kt +++ b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/domain/SaveInitialUserPreferencesUseCase.kt @@ -50,4 +50,4 @@ class SaveInitialUserPreferencesUseCase( ) userPreferencesRepository.updateUserPreferences(preferences) } -} \ No newline at end of file +} diff --git a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/navigation/OnboardingNavigation.kt b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/navigation/OnboardingNavigation.kt index a56b63c..0bd855a 100644 --- a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/navigation/OnboardingNavigation.kt +++ b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/navigation/OnboardingNavigation.kt @@ -51,4 +51,4 @@ fun NavGraphBuilder.onboardingNavigation( ) } } -} \ No newline at end of file +} diff --git a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/navigation/OnboardingRoutes.kt b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/navigation/OnboardingRoutes.kt index 71a1ad1..5be7585 100644 --- a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/navigation/OnboardingRoutes.kt +++ b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/navigation/OnboardingRoutes.kt @@ -32,4 +32,4 @@ object OnboardingRoutes { @Serializable data object Preferences -} \ No newline at end of file +} diff --git a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/presentation/PreferencesEvent.kt b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/presentation/PreferencesEvent.kt index edaa17a..b89de5a 100644 --- a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/presentation/PreferencesEvent.kt +++ b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/presentation/PreferencesEvent.kt @@ -3,4 +3,4 @@ package net.opatry.h2go.onboarding.presentation sealed interface PreferencesEvent { data object NavigateToMain : PreferencesEvent data class Error(val message: String): PreferencesEvent -} \ No newline at end of file +} diff --git a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/presentation/PreferencesUiState.kt b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/presentation/PreferencesUiState.kt index 68647d3..dba3430 100644 --- a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/presentation/PreferencesUiState.kt +++ b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/presentation/PreferencesUiState.kt @@ -31,4 +31,4 @@ data class PreferencesUiState( val notificationsFrequency: Duration, val notificationsFrequencyBounds: ClosedRange, val isSaving: Boolean, -) \ No newline at end of file +) diff --git a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/presentation/UserVolumeUnit.kt b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/presentation/UserVolumeUnit.kt index fcfd56a..f0ca7a1 100644 --- a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/presentation/UserVolumeUnit.kt +++ b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/presentation/UserVolumeUnit.kt @@ -19,4 +19,4 @@ sealed interface UserVolumeUnit { override val labelRes: Int get() = R.string.onboarding_preferences_volume_unit_oz } -} \ No newline at end of file +} diff --git a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/presentation/UserVolumeUnitMapper.kt b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/presentation/UserVolumeUnitMapper.kt index 5f9881d..e897ba3 100644 --- a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/presentation/UserVolumeUnitMapper.kt +++ b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/presentation/UserVolumeUnitMapper.kt @@ -12,4 +12,4 @@ class UserVolumeUnitMapper { UserVolumeUnit.Milliliter -> VolumeUnit.Milliliter UserVolumeUnit.Oz -> VolumeUnit.Oz } -} \ No newline at end of file +} diff --git a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/presentation/WelcomeUiState.kt b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/presentation/WelcomeUiState.kt index b031f24..a1c03c7 100644 --- a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/presentation/WelcomeUiState.kt +++ b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/presentation/WelcomeUiState.kt @@ -27,4 +27,4 @@ sealed interface WelcomeUiState { data object Idle : WelcomeUiState data object ShowWelcome : WelcomeUiState data object NavigateToMain : WelcomeUiState -} \ No newline at end of file +} diff --git a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/ui/PreferencesScreen.kt b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/ui/PreferencesScreen.kt index f8296b7..6f64f53 100644 --- a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/ui/PreferencesScreen.kt +++ b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/ui/PreferencesScreen.kt @@ -117,7 +117,7 @@ fun PreferencesScreen( } @Composable -fun PreferencesScreen( +internal fun PreferencesScreen( uiState: PreferencesUiState, onVolumeUnitSelected: (UserVolumeUnit) -> Unit, onNotificationsEnabledChanged: (Boolean) -> Unit, @@ -294,4 +294,4 @@ private fun PreferencesScreenPreview( ) } } -} \ No newline at end of file +} diff --git a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/ui/WelcomeScreen.kt b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/ui/WelcomeScreen.kt index 2a77970..2079563 100644 --- a/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/ui/WelcomeScreen.kt +++ b/onboarding/src/main/kotlin/net/opatry/h2go/onboarding/ui/WelcomeScreen.kt @@ -73,7 +73,7 @@ fun WelcomeScreen( } @Composable -fun WelcomeScreen( +internal fun WelcomeScreen( onContinueClicked: () -> Unit, ) { Column( @@ -114,4 +114,4 @@ private fun WelcomeScreenContentPreview() { ) } } -} \ No newline at end of file +} diff --git a/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/di/OnboardingModuleTest.kt b/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/di/OnboardingModuleTest.kt index 2840712..c85e18b 100644 --- a/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/di/OnboardingModuleTest.kt +++ b/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/di/OnboardingModuleTest.kt @@ -43,4 +43,4 @@ class OnboardingModuleTest { ) ) } -} \ No newline at end of file +} diff --git a/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/domain/CheckUserPreferencesExistUseCaseTest.kt b/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/domain/CheckUserPreferencesExistUseCaseTest.kt index 2af1688..6daa53d 100644 --- a/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/domain/CheckUserPreferencesExistUseCaseTest.kt +++ b/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/domain/CheckUserPreferencesExistUseCaseTest.kt @@ -75,4 +75,4 @@ class CheckUserPreferencesExistUseCaseTest { // Then assertThat(result).isFalse() } -} \ No newline at end of file +} diff --git a/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/domain/SaveInitialUserPreferencesUseCaseTest.kt b/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/domain/SaveInitialUserPreferencesUseCaseTest.kt index cc11840..63ddf94 100644 --- a/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/domain/SaveInitialUserPreferencesUseCaseTest.kt +++ b/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/domain/SaveInitialUserPreferencesUseCaseTest.kt @@ -98,4 +98,4 @@ class SaveInitialUserPreferencesUseCaseTest { ) ) } -} \ No newline at end of file +} diff --git a/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/presentation/PreferencesViewModelTest.kt b/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/presentation/PreferencesViewModelTest.kt index bed7a1f..6f92927 100644 --- a/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/presentation/PreferencesViewModelTest.kt +++ b/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/presentation/PreferencesViewModelTest.kt @@ -186,4 +186,4 @@ class PreferencesViewModelTest { ) assertThat(collectedEvents).containsExactly(PreferencesEvent.Error(expectedErrorMessage)) } -} \ No newline at end of file +} diff --git a/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/presentation/UserVolumeUnitMapperTest.kt b/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/presentation/UserVolumeUnitMapperTest.kt index 5bd0675..a3dd88d 100644 --- a/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/presentation/UserVolumeUnitMapperTest.kt +++ b/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/presentation/UserVolumeUnitMapperTest.kt @@ -60,4 +60,4 @@ class UserVolumeUnitMapperTest { // Then assertThat(unitLabelRes).isEqualTo(expectedLabelRes) } -} \ No newline at end of file +} diff --git a/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/presentation/WelcomeViewModelTest.kt b/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/presentation/WelcomeViewModelTest.kt index 0d77bdf..375ad40 100644 --- a/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/presentation/WelcomeViewModelTest.kt +++ b/onboarding/src/test/kotlin/net/opatry/h2go/onboarding/presentation/WelcomeViewModelTest.kt @@ -73,4 +73,4 @@ class WelcomeViewModelTest { advanceUntilIdle() assertThat(viewModel.uiState.value).isEqualTo(WelcomeUiState.ShowWelcome) } -} \ No newline at end of file +} diff --git a/preferences/src/main/kotlin/net/opatry/h2go/preference/data/UserPreferencesDao.kt b/preferences/src/main/kotlin/net/opatry/h2go/preference/data/UserPreferencesDao.kt index 7bfaceb..fe10121 100644 --- a/preferences/src/main/kotlin/net/opatry/h2go/preference/data/UserPreferencesDao.kt +++ b/preferences/src/main/kotlin/net/opatry/h2go/preference/data/UserPreferencesDao.kt @@ -46,4 +46,4 @@ interface UserPreferencesDao { clear() return upsert(defaultPreferences) } -} \ No newline at end of file +} diff --git a/preferences/src/main/kotlin/net/opatry/h2go/preference/data/UserPreferencesMapper.kt b/preferences/src/main/kotlin/net/opatry/h2go/preference/data/UserPreferencesMapper.kt index 652b28e..4c74ae0 100644 --- a/preferences/src/main/kotlin/net/opatry/h2go/preference/data/UserPreferencesMapper.kt +++ b/preferences/src/main/kotlin/net/opatry/h2go/preference/data/UserPreferencesMapper.kt @@ -44,4 +44,4 @@ class UserPreferencesMapper { areNotificationsEnabled = preferences.areNotificationsEnabled, notificationFrequencyInHours = preferences.notificationsFrequency.inWholeHours.toInt(), ) -} \ No newline at end of file +} diff --git a/preferences/src/main/kotlin/net/opatry/h2go/preference/data/UserPreferencesRepositoryImpl.kt b/preferences/src/main/kotlin/net/opatry/h2go/preference/data/UserPreferencesRepositoryImpl.kt index f9c1a13..2a31e31 100644 --- a/preferences/src/main/kotlin/net/opatry/h2go/preference/data/UserPreferencesRepositoryImpl.kt +++ b/preferences/src/main/kotlin/net/opatry/h2go/preference/data/UserPreferencesRepositoryImpl.kt @@ -47,4 +47,4 @@ class UserPreferencesRepositoryImpl( override suspend fun resetUserPreferences(defaultValue: UserPreferences) { dao.reset(mapper.toEntity(defaultValue)) } -} \ No newline at end of file +} diff --git a/preferences/src/main/kotlin/net/opatry/h2go/preference/data/entity/UserPreferencesEntity.kt b/preferences/src/main/kotlin/net/opatry/h2go/preference/data/entity/UserPreferencesEntity.kt index e95d487..0df0011 100644 --- a/preferences/src/main/kotlin/net/opatry/h2go/preference/data/entity/UserPreferencesEntity.kt +++ b/preferences/src/main/kotlin/net/opatry/h2go/preference/data/entity/UserPreferencesEntity.kt @@ -33,4 +33,4 @@ data class UserPreferencesEntity( val volumeUnit: String, val areNotificationsEnabled: Boolean, val notificationFrequencyInHours: Int, -) \ No newline at end of file +) diff --git a/preferences/src/main/kotlin/net/opatry/h2go/preference/di/preferencesModule.kt b/preferences/src/main/kotlin/net/opatry/h2go/preference/di/preferencesModule.kt index 47b51b7..2e61a18 100644 --- a/preferences/src/main/kotlin/net/opatry/h2go/preference/di/preferencesModule.kt +++ b/preferences/src/main/kotlin/net/opatry/h2go/preference/di/preferencesModule.kt @@ -35,4 +35,4 @@ val preferencesModule = module { singleOf(::UserPreferencesRepositoryImpl) bind UserPreferencesRepository::class -} \ No newline at end of file +} diff --git a/preferences/src/main/kotlin/net/opatry/h2go/preference/domain/UserPreferencesRepository.kt b/preferences/src/main/kotlin/net/opatry/h2go/preference/domain/UserPreferencesRepository.kt index ede5214..dba83a8 100644 --- a/preferences/src/main/kotlin/net/opatry/h2go/preference/domain/UserPreferencesRepository.kt +++ b/preferences/src/main/kotlin/net/opatry/h2go/preference/domain/UserPreferencesRepository.kt @@ -30,4 +30,4 @@ interface UserPreferencesRepository { suspend fun updateUserPreferences(preferences: UserPreferences) suspend fun resetUserPreferences(defaultValue: UserPreferences) -} \ No newline at end of file +} diff --git a/preferences/src/main/kotlin/net/opatry/h2go/preference/domain/VolumeUnit.kt b/preferences/src/main/kotlin/net/opatry/h2go/preference/domain/VolumeUnit.kt index fb8ff80..e29fbb3 100644 --- a/preferences/src/main/kotlin/net/opatry/h2go/preference/domain/VolumeUnit.kt +++ b/preferences/src/main/kotlin/net/opatry/h2go/preference/domain/VolumeUnit.kt @@ -25,4 +25,4 @@ package net.opatry.h2go.preference.domain enum class VolumeUnit { Milliliter, Oz, -} \ No newline at end of file +} diff --git a/preferences/src/test/kotlin/net/opatry/h2go/preference/data/UserPreferencesDaoTest.kt b/preferences/src/test/kotlin/net/opatry/h2go/preference/data/UserPreferencesDaoTest.kt index 0299359..7cf66fa 100644 --- a/preferences/src/test/kotlin/net/opatry/h2go/preference/data/UserPreferencesDaoTest.kt +++ b/preferences/src/test/kotlin/net/opatry/h2go/preference/data/UserPreferencesDaoTest.kt @@ -165,4 +165,4 @@ class UserPreferencesDaoTest { val resultingPreferences = dao.getUserPreferences().first() assertThat(resultingPreferences).isEqualTo(defaultPreferences) } -} \ No newline at end of file +} diff --git a/preferences/src/test/kotlin/net/opatry/h2go/preference/data/UserPreferencesMapperTest.kt b/preferences/src/test/kotlin/net/opatry/h2go/preference/data/UserPreferencesMapperTest.kt index b8609df..a984d7a 100644 --- a/preferences/src/test/kotlin/net/opatry/h2go/preference/data/UserPreferencesMapperTest.kt +++ b/preferences/src/test/kotlin/net/opatry/h2go/preference/data/UserPreferencesMapperTest.kt @@ -100,4 +100,4 @@ class UserPreferencesMapperTest { assertThat(result.areNotificationsEnabled).isEqualTo(areNotificationsEnabled) assertThat(result.notificationFrequencyInHours).isEqualTo(3) } -} \ No newline at end of file +} diff --git a/preferences/src/test/kotlin/net/opatry/h2go/preference/data/UserPreferencesRepositoryTest.kt b/preferences/src/test/kotlin/net/opatry/h2go/preference/data/UserPreferencesRepositoryTest.kt index 56bd64f..0ec959b 100644 --- a/preferences/src/test/kotlin/net/opatry/h2go/preference/data/UserPreferencesRepositoryTest.kt +++ b/preferences/src/test/kotlin/net/opatry/h2go/preference/data/UserPreferencesRepositoryTest.kt @@ -133,4 +133,4 @@ class UserPreferencesRepositoryTest { val resultingPreferences = repository.getUserPreferences().first() assertThat(resultingPreferences).isEqualTo(defaultPreferences) } -} \ No newline at end of file +} diff --git a/preferences/src/test/kotlin/net/opatry/h2go/preference/data/UserPreferencesTestDatabase.kt b/preferences/src/test/kotlin/net/opatry/h2go/preference/data/UserPreferencesTestDatabase.kt index 3d69da9..68b1369 100644 --- a/preferences/src/test/kotlin/net/opatry/h2go/preference/data/UserPreferencesTestDatabase.kt +++ b/preferences/src/test/kotlin/net/opatry/h2go/preference/data/UserPreferencesTestDatabase.kt @@ -29,4 +29,4 @@ import net.opatry.h2go.preference.data.entity.UserPreferencesEntity @Database(entities = [UserPreferencesEntity::class], version = 1, exportSchema = false) abstract class UserPreferencesTestDatabase : RoomDatabase() { abstract fun userPreferencesDao(): UserPreferencesDao -} \ No newline at end of file +} diff --git a/preferences/src/test/kotlin/net/opatry/h2go/preference/di/PreferencesModuleTest.kt b/preferences/src/test/kotlin/net/opatry/h2go/preference/di/PreferencesModuleTest.kt index 7e7efaa..b190583 100644 --- a/preferences/src/test/kotlin/net/opatry/h2go/preference/di/PreferencesModuleTest.kt +++ b/preferences/src/test/kotlin/net/opatry/h2go/preference/di/PreferencesModuleTest.kt @@ -41,4 +41,4 @@ class PreferencesModuleTest { ) ) } -} \ No newline at end of file +} diff --git a/settings.gradle.kts b/settings.gradle.kts index f89e5eb..cf055f6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -42,8 +42,9 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") rootProject.name = "H2Go" +include(":detekt-rules") include(":h2go-app") -include(":preferences") +include(":preferences") include(":onboarding") include(":android-test-util") include(":test-util") diff --git a/test-util/build.gradle.kts b/test-util/build.gradle.kts index 47b198a..27947c9 100644 --- a/test-util/build.gradle.kts +++ b/test-util/build.gradle.kts @@ -21,7 +21,7 @@ */ plugins { - id("kotlin") + kotlin("jvm") } kotlin { diff --git a/test-util/src/main/kotlin/net/opatry/test/util/MainDispatcherExtension.kt b/test-util/src/main/kotlin/net/opatry/test/util/MainDispatcherExtension.kt index ede819f..0b97629 100644 --- a/test-util/src/main/kotlin/net/opatry/test/util/MainDispatcherExtension.kt +++ b/test-util/src/main/kotlin/net/opatry/test/util/MainDispatcherExtension.kt @@ -11,10 +11,12 @@ import org.junit.jupiter.api.extension.ExtensionContext @ExperimentalCoroutinesApi class MainDispatcherExtension : BeforeAllCallback, AfterAllCallback { + override fun beforeAll(context: ExtensionContext?) { Dispatchers.setMain(StandardTestDispatcher()) } + override fun afterAll(context: ExtensionContext?) { Dispatchers.resetMain() } -} \ No newline at end of file +}