Skip to content

Commit

Permalink
Merge pull request #3947 from element-hq/renovate/com.lemonappdev-kon…
Browse files Browse the repository at this point in the history
…sist-0.x

Update dependency com.lemonappdev:konsist to v0.17.0
  • Loading branch information
bmarty authored Nov 26, 2024
2 parents b2a79d2 + c209245 commit c678406
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 19 deletions.
5 changes: 5 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ allprojects {
detektPlugins("io.nlopez.compose.rules:detekt:0.4.19")
}

tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
exclude("io/element/android/tests/konsist/failures/**")
}

// KtLint
apply {
plugin("org.jlleitschuh.gradle.ktlint")
Expand All @@ -75,6 +79,7 @@ allprojects {
val generatedPath = "${layout.buildDirectory.asFile.get()}/generated/"
filter {
exclude { element -> element.file.path.contains(generatedPath) }
exclude("io/element/android/tests/konsist/failures/**")
}
}
// Dependency check
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ test_arch_core = "androidx.arch.core:core-testing:2.2.0"
test_junit = "junit:junit:4.13.2"
test_runner = "androidx.test:runner:1.6.2"
test_mockk = "io.mockk:mockk:1.13.13"
test_konsist = "com.lemonappdev:konsist:0.16.1"
test_konsist = "com.lemonappdev:konsist:0.17.0"
test_turbine = "app.cash.turbine:turbine:1.2.0"
test_truth = "com.google.truth:truth:1.4.4"
test_parameter_injector = "com.google.testparameterinjector:test-parameter-injector:1.18"
Expand Down
2 changes: 2 additions & 0 deletions plugins/src/main/kotlin/extension/KoverExtension.kt
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ fun Project.setupKover() {
"io.element.android.libraries.designsystem.theme.components.bottomsheet.*",
// Test presenters
"io.element.android.features.leaveroom.fake.FakeLeaveRoomPresenter",
// Konsist code to make test fails
"io.element.android.tests.konsist.failures",
)
annotatedBy(
"androidx.compose.ui.tooling.preview.Preview",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/

package io.element.android.tests.konsist.failures

import androidx.compose.runtime.Composable

// Make test `Sealed interface used in Composable MUST be Immutable or Stable` fails

sealed interface SealedInterface

@Composable
fun FailingComposableWithNonImmutableSealedInterface(
sealedInterface: SealedInterface
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/

package io.element.android.tests.konsist.failures

// Make test `Fake classes must be named using Fake and the interface it fakes` fails

interface MyInterface

// This class should be named FakeMyInterface
class FakeWrongClassName : MyInterface

class MyClass {
interface MyFactory
}

// This class should be named FakeMyClassMyFactory
class FakeWrongClassSubInterfaceName : MyClass.MyFactory
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.lemonappdev.konsist.api.ext.list.withoutName
import com.lemonappdev.konsist.api.ext.list.withoutParents
import com.lemonappdev.konsist.api.verify.assertEmpty
import com.lemonappdev.konsist.api.verify.assertTrue
import org.junit.Assert.assertTrue
import org.junit.Test

class KonsistArchitectureTest {
Expand Down Expand Up @@ -66,34 +67,44 @@ class KonsistArchitectureTest {

@Test
fun `Sealed interface used in Composable MUST be Immutable or Stable`() {
var failingTestFound = false
// List all sealed interface without Immutable nor Stable annotation in the project
val forbiddenInterfacesForComposableParameter = Konsist.scopeFromProject()
.interfaces()
.withSealedModifier()
.withoutAnnotationOf(Immutable::class, Stable::class)
.map { it.fullyQualifiedName }

Konsist.scopeFromProject()
.functions()
.withAnnotationOf(Composable::class)
.assertTrue(additionalMessage = "Consider adding the @Immutable or @Stable annotation to the sealed interface") {
it.parameters.all { param ->
val result = it.parameters.all { param ->
val type = param.type.text
return@all if (type.startsWith("@") || type.startsWith("(") || type.startsWith("suspend")) {
return@all if (type.startsWith("@") || type.contains("->") || type.startsWith("suspend")) {
true
} else {
var typePackage = param.type.declaration.packagee?.name
if (typePackage == type) {
// Workaround, now that packagee.fullyQualifiedName is not available anymore
// It seems that when the type in in the same package as the function,
// the package is equal to the type (which is wrong).
// So in this case, use the package of the function
typePackage = it.packagee?.name
val typePackage = param.type.sourceDeclaration?.let { declaration ->
declaration.asTypeParameterDeclaration()?.packagee
?: declaration.asExternalDeclaration()?.packagee
?: declaration.asClassOrInterfaceDeclaration()?.packagee
?: declaration.asKotlinTypeDeclaration()?.packagee
?: declaration.asObjectDeclaration()?.packagee
}?.name
if (typePackage == null) {
false
} else {
val fullyQualifiedName = "$typePackage.$type"
fullyQualifiedName !in forbiddenInterfacesForComposableParameter
}
val fullyQualifiedName = "$typePackage.$type"
fullyQualifiedName !in forbiddenInterfacesForComposableParameter
}
}
if (!result && !failingTestFound && it.name == "FailingComposableWithNonImmutableSealedInterface") {
failingTestFound = true
true
} else {
result
}
}
assertTrue("FailingComposableWithNonImmutableSealedInterface should make this test fail.", failingTestFound)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ class KonsistClassNameTest {

@Test
fun `Fake classes must be named using Fake and the interface it fakes`() {
var failingCases = 0
val failingCasesList = listOf(
"FakeWrongClassName",
"FakeWrongClassSubInterfaceName",
)
Konsist.scopeFromProject()
.classes()
.withNameContaining("Fake")
Expand All @@ -84,16 +89,19 @@ class KonsistClassNameTest {
val interfaceName = it.name
.replace("FakeRust", "")
.replace("Fake", "")
(it.name.startsWith("Fake") || it.name.startsWith("FakeRust")) &&
val result = (it.name.startsWith("Fake") || it.name.startsWith("FakeRust")) &&
it.parents().any { parent ->
// Workaround to get the parent name. For instance:
// parent.name used to return `UserListPresenter.Factory` but is now returning `Factory`.
// So we need to retrieve the name of the parent class differently.
val packageName = parent.packagee!!.name
val parentName = parent.fullyQualifiedName!!.substringAfter("$packageName.").replace(".", "")
val parentName = parent.name.replace(".", "")
parentName == interfaceName
}
if (!result && it.name in failingCasesList) {
failingCases++
true
} else {
result
}
}
assertThat(failingCases).isEqualTo(failingCasesList.size)
}

@Test
Expand Down

0 comments on commit c678406

Please sign in to comment.