| /* |
| * Copyright 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| @file:Suppress("UnstableApiUsage") |
| |
| package androidx.compose.lint |
| |
| import com.android.tools.lint.checks.infrastructure.LintDetectorTest |
| import com.android.tools.lint.detector.api.Detector |
| import com.android.tools.lint.detector.api.Issue |
| import org.junit.Test |
| import org.junit.runner.RunWith |
| import org.junit.runners.JUnit4 |
| |
| /* ktlint-disable max-line-length */ |
| @RunWith(JUnit4::class) |
| class ModifierInspectorInfoDetectorTest : LintDetectorTest() { |
| override fun getDetector(): Detector = ModifierInspectorInfoDetector() |
| |
| override fun getIssues(): List<Issue> = listOf(ModifierInspectorInfoDetector.ISSUE) |
| |
| private val inspectableInfoStub = kotlin( |
| """ |
| package androidx.compose.ui.platform |
| |
| val NoInspectorInfo: InspectorInfo.() -> Unit = {} |
| val DebugInspectorInfo = false |
| |
| interface InspectableValue { |
| val inspectableElements: Sequence<ValueElement> |
| get() = emptySequence() |
| |
| val nameFallback: String? |
| get() = null |
| |
| val valueOverride: Any? |
| get() = null |
| } |
| |
| data class ValueElement(val name: String, val value: Any?) |
| |
| class InspectorInfo { |
| var name: String? = null |
| var value: Any? = null |
| val properties = ValueElementSequence() |
| } |
| |
| class ValueElementSequence : Sequence<ValueElement> { |
| private val elements = mutableListOf<ValueElement>() |
| |
| override fun iterator(): Iterator<ValueElement> = elements.iterator() |
| |
| operator fun set(name: String, value: Any?) { |
| elements.add(ValueElement(name, value)) |
| } |
| } |
| |
| abstract class InspectorValueInfo( |
| private val info: InspectorInfo.() -> Unit |
| ) : InspectableValue { |
| private var _values: InspectorInfo? = null |
| |
| private val values: InspectorInfo |
| get() { |
| val valueInfo = _values ?: InspectorInfo().apply { info() } |
| _values = valueInfo |
| return valueInfo |
| } |
| |
| override val nameFallback: String? |
| get() = values.name |
| |
| override val valueOverride: Any? |
| get() = values.value |
| |
| override val inspectableElements: Sequence<ValueElement> |
| get() = values.properties |
| } |
| |
| inline fun debugInspectorInfo( |
| crossinline definitions: InspectorInfo.() -> Unit |
| ): InspectorInfo.() -> Unit = |
| if (DebugInspectorInfo) ({ definitions() }) else NoInspectorInfo |
| |
| """ |
| ).indented() |
| |
| private val composedStub = kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| |
| fun Modifier.composed( |
| inspectorInfo: InspectorInfo.() -> Unit = NoInspectorInfo, |
| factory: Modifier.() -> Modifier |
| ): Modifier = this.then(ComposedModifier(inspectorInfo, factory)) |
| |
| private class ComposedModifier( |
| inspectorInfo: InspectorInfo.() -> Unit, |
| val factory: Modifier.() -> Modifier |
| ) : Modifier.Element, InspectorValueInfo(inspectorInfo) |
| """ |
| ).indented() |
| |
| @Test |
| fun existingInspectorInfo() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| inline class Dp(val value: Float) |
| |
| fun Modifier.width1(width: Dp) = |
| this.then(SizeModifier1(width, inspectorInfo = debugInspectorInfo { |
| name = "width1" |
| properties["width"] = width |
| })) |
| |
| private class SizeModifier1( |
| val width: Int, |
| inspectorInfo: InspectorInfo.() -> Unit |
| ): Modifier.Element, InspectorValueInfo(inspectorInfo) |
| |
| """ |
| ).indented() |
| ) |
| .run() |
| .expectClean() |
| } |
| |
| @Test |
| fun existingInspectorInfoWithStatementsBeforeDefinition() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| inline class Dp(val value: Float) |
| |
| inline fun require(value: Boolean, lazyMessage: () -> String) { |
| if (!value) { |
| val message = lazyMessage() |
| throw IllegalArgumentException(message) |
| } |
| } |
| |
| fun Modifier.width1(width: Dp): Modifier { |
| require(width.value > 0.0f) { return "sds" } |
| |
| val x = width.value.toInt() * 2 |
| for (i in 0..4) { |
| println("x = " + x) |
| } |
| |
| return this.then(SizeModifier1(x, inspectorInfo = debugInspectorInfo { |
| name = "width1" |
| properties["width"] = width |
| })) |
| } |
| |
| private class SizeModifier1( |
| val width: Int, |
| inspectorInfo: InspectorInfo.() -> Unit |
| ): Modifier.Element, InspectorValueInfo(inspectorInfo) |
| |
| """ |
| ).indented() |
| ) |
| .run() |
| .expectClean() |
| } |
| |
| @Test |
| fun existingInspectorInfoWithValue() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| fun Modifier.width2(width: Int) = |
| this.then(SizeModifier2(width, inspectorInfo = debugInspectorInfo { |
| name = "width2" |
| value = width |
| })) |
| |
| private class SizeModifier2( |
| val width: Int, |
| inspectorInfo: InspectorInfo.() -> Unit |
| ): Modifier.Element, InspectorValueInfo(inspectorInfo) { |
| } |
| |
| """ |
| ).indented() |
| ) |
| .run() |
| .expectClean() |
| } |
| |
| @Test |
| fun existingInspectorInfoViaSynonym() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| inline class Dp(val value: Float) |
| |
| fun Modifier.width1(width: Dp) = |
| this.then(SizeModifier1(width, inspectorInfo = debugInspectorInfo { |
| name = "width1" |
| properties["width"] = width |
| })) |
| |
| fun Modifier.width2(width: Dp) = width1(width) |
| |
| fun Modifier.width20() = width1(Dp(20.0f)) |
| |
| fun Modifier.preferredIconWidth(x: Int) = this.then( |
| if (x == 7) DefaultIconSizeModifier else Modifier |
| ) |
| |
| private val DefaultIconSizeModifier = Modifier.width1(Dp(24.0f)) |
| |
| private class SizeModifier1( |
| val width: Int, |
| inspectorInfo: InspectorInfo.() -> Unit |
| ): Modifier.Element, InspectorValueInfo(inspectorInfo) |
| """ |
| ).indented() |
| ) |
| .run() |
| .expectClean() |
| } |
| |
| @Test |
| fun existingInspectorInfoWithAnonymousClass() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| fun Modifier.drawBehind() = this.then( |
| object : Modifier, |
| InspectorValueInfo(debugInspectorInfo { name = "drawBehind" }) {} |
| ) |
| """ |
| ).indented() |
| ) |
| .run() |
| .expectClean() |
| } |
| |
| @Test |
| fun existingInspectorInfoWithDataClassMemberValues() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| fun Modifier.border(values: Borders) = |
| this.then(BorderModifier(values, inspectorInfo = debugInspectorInfo { |
| name = "border" |
| properties["start"] = values.start |
| properties["top"] = values.top |
| properties["end"] = values.end |
| properties["bottom"] = values.bottom |
| })) |
| |
| fun Modifier.border2(start: Int, top: Int, end: Int, bottom: Int) = |
| this.then( |
| BorderModifier2( |
| start, top, end, bottom, inspectorInfo = debugInspectorInfo { |
| name = "border2" |
| properties["start"] = start |
| properties["top"] = top |
| properties["end"] = end |
| properties["bottom"] = bottom |
| })) |
| |
| fun Modifier.border2(values: Borders) = |
| border2(values.start, values.top, values.end, values.bottom) |
| |
| fun Modifier.border3(corner1: Location, corner2: Location) = |
| border2(corner1.x, corner1.y, corner2.x, corner2.y) |
| |
| private class BorderModifier( |
| val values: Borders, |
| inspectorInfo: InspectorInfo.() -> Unit |
| ): Modifier.Element, InspectorValueInfo(inspectorInfo) { |
| } |
| |
| private class BorderModifier2( |
| val start: Int, |
| val top: Int, |
| val end: Int, |
| val bottom: Int, |
| inspectorInfo: InspectorInfo.() -> Unit |
| ): Modifier.Element, InspectorValueInfo(inspectorInfo) { |
| } |
| |
| data class Borders(val start: Int, val top: Int, val end: Int, val bottom: Int) { |
| constructor(all: Int) : this(all, all, all, all) |
| } |
| |
| data class Location(val x: Int, val y: Int) |
| """ |
| ).indented() |
| ) |
| .run() |
| .expectClean() |
| } |
| |
| @Test |
| fun existingInspectorInfoWithConditional() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| fun Modifier.padding(size: Int) = |
| this.then( |
| if (size >= 10) { |
| PaddingModifier( |
| paddingSize = size, |
| inspectorInfo = debugInspectorInfo { |
| name = "padding" |
| properties["size"] = size |
| } |
| ) |
| } else { |
| Modifier |
| } |
| ) |
| |
| fun Modifier.paddingFromBaseline(top: Int, bottom: Int) = this |
| .then(if (bottom > 0) padding(bottom) else Modifier) |
| .then(if (top > 0) padding(top) else Modifier) |
| |
| fun Modifier.paddingFromBaseline2(top: Int, bottom: Int) = |
| this.padding(bottom).padding(top) |
| |
| private class PaddingModifier( |
| paddingSize: Int, |
| inspectorInfo: InspectorInfo.() -> Unit |
| ): Modifier.Element, InspectorValueInfo(inspectorInfo) { |
| } |
| |
| """ |
| ).indented() |
| ) |
| .run() |
| .expectClean() |
| } |
| |
| @Test |
| fun existingInspectorInfoWithWhen() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| fun Modifier.border(painter: Painter) = |
| this.then( |
| when (painter.size) { |
| 0 -> Modifier |
| 1 -> BorderModifier(inspectorInfo = debugInspectorInfo { |
| name = "border" |
| properties["painter"] = painter |
| }) |
| else -> Modifier |
| } |
| ) |
| |
| private class BorderModifier( |
| inspectorInfo: InspectorInfo.() -> Unit |
| ): Modifier.Element, InspectorValueInfo(inspectorInfo) { |
| } |
| |
| class Painter(val size: Int) |
| |
| """ |
| ).indented() |
| ) |
| .run() |
| .expectClean() |
| } |
| |
| @Test |
| fun existingInspectorInfoWithConditionals() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| class Brush |
| |
| class SolidColor(val color: Int): Brush() |
| |
| fun Modifier.border(width: Int, brush: Brush, shape: Shape): Modifier = composed( |
| factory = { BorderModifier(shape, width, brush) }, |
| inspectorInfo = debugInspectorInfo { |
| name = "border" |
| properties["width"] = width |
| if (brush is SolidColor) { |
| properties["color"] = brush.value |
| value = brush.value |
| } else { |
| properties["brush"] = brush |
| } |
| properties["shape"] = shape |
| } |
| ) |
| |
| fun Modifier.border2(width: Int, color: Int, shape: Shape): Modifier = |
| if (width > 0) { |
| composed( |
| inspectorInfo = debugInspectorInfo { |
| name = "border2" |
| properties["width"] = width |
| properties["color"] = color |
| properties["shape"] = shape |
| } |
| ) { |
| border(width, SolidColor(color), shape) |
| } |
| } else { |
| this |
| } |
| |
| private class BorderModifier(shape: Shape, width: Int, brush: Brush) |
| |
| """ |
| ).indented() |
| ) |
| .run() |
| .expectClean() |
| } |
| |
| @Test |
| fun composedModifierWithInspectorInfo() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| fun Modifier.border(width: Int): Modifier = composed( |
| inspectorInfo = debugInspectorInfo { |
| name = "border" |
| properties["width"] = width |
| }, |
| factory = { this.then(BorderModifier(width)) } |
| ) |
| |
| fun Modifier.border2(width: Int): Modifier = composed( |
| factory = { this.then(BorderModifier(width)) }, |
| inspectorInfo = debugInspectorInfo { |
| name = "border2" |
| properties["width"] = width |
| } |
| ) |
| |
| private class BorderModifier(private val width: Int): Modifier.Element { |
| } |
| |
| """ |
| ).indented() |
| ) |
| .run() |
| .expectClean() |
| } |
| |
| @Test |
| fun rememberModifierInfo() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| kotlin(Stubs.Remember), |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.runtime.remember |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| fun Modifier.width1(width: Int) = this.then( |
| remember { |
| SizeModifier1(width, inspectorInfo = debugInspectorInfo { |
| name = "width1" |
| properties["width"] = width |
| }) |
| } |
| ) |
| |
| private class SizeModifier1( |
| val width: Int, |
| inspectorInfo: InspectorInfo.() -> Unit |
| ): Modifier.Element, InspectorValueInfo(inspectorInfo) |
| |
| """ |
| ).indented() |
| ) |
| .run() |
| .expectClean() |
| } |
| |
| @Test |
| fun emptyModifier() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.runtime.remember |
| import androidx.compose.ui.Modifier |
| |
| internal actual fun Modifier.width1(width: Int): Modifier = this |
| """ |
| ).indented() |
| ) |
| .run() |
| .expectClean() |
| } |
| |
| @Test |
| fun acceptMissingInspectorInfoInSamples() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui.demos.whatever |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| fun Modifier.width2(width: Int) = this.then(SizeModifier2(width)) |
| |
| private data class SizeModifier2( |
| val width: Int, |
| ): Modifier.Element |
| |
| """ |
| ).indented() |
| ) |
| .run() |
| .expectClean() |
| } |
| |
| @Test |
| fun missingInspectorInfo() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| fun Modifier.preferredWidth2(width: Int) = this.then(SizeModifier2(width)) |
| |
| private data class SizeModifier2( |
| val width: Int, |
| ): Modifier.Element |
| |
| """ |
| ).indented() |
| ) |
| .run() |
| .expect( |
| """ |
| src/androidx/compose/ui/SizeModifier2.kt:8: Error: Modifier missing inspectorInfo [ModifierInspectorInfo] |
| fun Modifier.preferredWidth2(width: Int) = this.then(SizeModifier2(width)) |
| ~~~~~~~~~~~~~ |
| 1 errors, 0 warnings |
| """ |
| ) |
| } |
| |
| @Test |
| fun composedModifierWithMissingInspectorInfo() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| fun Modifier.border(width: Int): Modifier = |
| composed { this.then(BorderModifier(width)) } |
| |
| private class BorderModifier(private val width: Int): Modifier.Element { |
| } |
| |
| """ |
| ).indented() |
| ) |
| .run() |
| .expect( |
| """ |
| src/androidx/compose/ui/BorderModifier.kt:9: Error: Modifier missing inspectorInfo [ModifierInspectorInfo] |
| composed { this.then(BorderModifier(width)) } |
| ~~~~~~~~ |
| 1 errors, 0 warnings |
| """ |
| ) |
| } |
| |
| @Test |
| fun missingInspectorInfoFromInnerClassImplementation() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| /** |
| * Documentation |
| */ |
| fun Modifier.size(width: Int) = |
| this.then(SizeModifier.WithOption(width)) |
| |
| internal sealed class SizeModifier : Modifier.Element { |
| internal data class WithOption(val width: Int) : SizeModifier() { |
| } |
| } |
| |
| """ |
| ).indented() |
| ) |
| .run() |
| .expect( |
| """ |
| src/androidx/compose/ui/SizeModifier.kt:12: Error: Modifier missing inspectorInfo [ModifierInspectorInfo] |
| this.then(SizeModifier.WithOption(width)) |
| ~~~~~~~~~~ |
| 1 errors, 0 warnings |
| """ |
| ) |
| } |
| |
| @Test |
| fun inspectorInfoWithWrongName() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| fun Modifier.width1(width: Int) = |
| this.then(SizeModifier1(width, inspectorInfo = debugInspectorInfo { |
| name = "otherName" |
| value = width |
| })) |
| |
| private class SizeModifier1( |
| val width: Int, |
| inspectorInfo: InspectorInfo.() -> Unit |
| ): Modifier.Element, InspectorValueInfo(inspectorInfo) { |
| } |
| |
| """ |
| ).indented() |
| ) |
| .run() |
| .expect( |
| """ |
| src/androidx/compose/ui/SizeModifier1.kt:10: Error: Expected name of the modifier: "name" = "width1" [ModifierInspectorInfo] |
| name = "otherName" |
| ~~~~~~~~~ |
| 1 errors, 0 warnings |
| """ |
| ) |
| } |
| |
| @Test |
| fun inspectorInfoWithWrongValue() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| fun Modifier.width1(width: Int) = |
| this.then(SizeModifier1(width, inspectorInfo = debugInspectorInfo { |
| name = "width1" |
| value = 3.4 |
| })) |
| |
| private class SizeModifier1( |
| val width: Int, |
| inspectorInfo: InspectorInfo.() -> Unit |
| ): Modifier.Element, InspectorValueInfo(inspectorInfo) { |
| } |
| |
| """ |
| ).indented() |
| ) |
| .run() |
| .expect( |
| """ |
| src/androidx/compose/ui/SizeModifier1.kt:11: Error: Expected the variable: "width" [ModifierInspectorInfo] |
| value = 3.4 |
| ~~~ |
| 1 errors, 0 warnings |
| """ |
| ) |
| } |
| |
| @Test |
| fun inspectorInfoWithWrongValueWhenMultipleAreAvailable() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| fun Modifier.width1(width: Int, height: Int) = |
| this.then(SizeModifier1(width, inspectorInfo = debugInspectorInfo { |
| name = "width1" |
| value = "oldWidth" |
| })) |
| |
| private class SizeModifier1( |
| val width: Int, |
| inspectorInfo: InspectorInfo.() -> Unit |
| ): Modifier.Element, InspectorValueInfo(inspectorInfo) { |
| } |
| |
| """ |
| ).indented() |
| ) |
| .run() |
| .expect( |
| """ |
| src/androidx/compose/ui/SizeModifier1.kt:11: Error: Expected one of the variables: "width, height" [ModifierInspectorInfo] |
| value = "oldWidth" |
| ~~~~~~~~ |
| 1 errors, 0 warnings |
| """ |
| ) |
| } |
| |
| @Test |
| fun inspectorInfoWithWrongParameterNameInProperties() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| fun Modifier.width1(width: Int, height: Int) = |
| this.then(SizeModifier1(width, inspectorInfo = debugInspectorInfo { |
| name = "width1" |
| properties["width"] = width |
| properties["other"] = height |
| })) |
| |
| private class SizeModifier1( |
| val width: Int, |
| inspectorInfo: InspectorInfo.() -> Unit |
| ): Modifier.Element, InspectorValueInfo(inspectorInfo) { |
| } |
| |
| """ |
| ).indented() |
| ) |
| .run() |
| .expect( |
| """ |
| src/androidx/compose/ui/SizeModifier1.kt:12: Error: Expected one of the variables: "width, height" [ModifierInspectorInfo] |
| properties["other"] = height |
| ~~~~~ |
| 1 errors, 0 warnings |
| """ |
| ) |
| } |
| |
| @Test |
| fun inspectorInfoWithMismatchInProperties() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| fun Modifier.width1(width: Int, height: Int) = |
| this.then(SizeModifier1(width, inspectorInfo = debugInspectorInfo { |
| name = "width1" |
| properties["height"] = width |
| properties["width"] = height |
| })) |
| |
| private class SizeModifier1( |
| val width: Int, |
| inspectorInfo: InspectorInfo.() -> Unit |
| ): Modifier.Element, InspectorValueInfo(inspectorInfo) { |
| } |
| """ |
| ).indented() |
| ) |
| .run() |
| .expect( |
| """ |
| src/androidx/compose/ui/SizeModifier1.kt:11: Error: The value should match the index name: height [ModifierInspectorInfo] |
| properties["height"] = width |
| ~~~~~ |
| 1 errors, 0 warnings |
| """ |
| ) |
| } |
| |
| @Test |
| fun inspectorInfoWithMissingDebugSelector() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| |
| fun Modifier.width1(width: Int, height: Int) = |
| this.then(SizeModifier1(width, height, inspectorInfo = { |
| name = "width1" |
| properties["width"] = width |
| properties["height"] = height |
| })) |
| |
| private class SizeModifier1( |
| val width: Int, |
| val height: Int, |
| inspectorInfo: InspectorInfo.() -> Unit |
| ): Modifier.Element, InspectorValueInfo(inspectorInfo) { |
| } |
| |
| """ |
| ).indented() |
| ) |
| .run() |
| .expect( |
| """ |
| src/androidx/compose/ui/SizeModifier1.kt:8: Error: Expected debugInspectorInfo call [ModifierInspectorInfo] |
| this.then(SizeModifier1(width, height, inspectorInfo = { |
| ^ |
| 1 errors, 0 warnings |
| """ |
| ) |
| } |
| |
| @Test |
| fun inspectorInfoWithMissingName() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| fun Modifier.width1(width: Int) = |
| this.then(SizeModifier1(width, inspectorInfo = debugInspectorInfo { |
| value = width |
| })) |
| |
| private class SizeModifier1( |
| val width: Int, |
| inspectorInfo: InspectorInfo.() -> Unit |
| ): Modifier.Element, InspectorValueInfo(inspectorInfo) { |
| } |
| |
| """ |
| ).indented() |
| ) |
| .run() |
| .expect( |
| """ |
| src/androidx/compose/ui/SizeModifier1.kt:9: Error: Expected name of the modifier: "name" = "width1" [ModifierInspectorInfo] |
| this.then(SizeModifier1(width, inspectorInfo = debugInspectorInfo { |
| ^ |
| 1 errors, 0 warnings |
| """ |
| ) |
| } |
| |
| @Test |
| fun inspectorInfoWithMissingVariables() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| fun Modifier.border(start: Int, top: Int, end: Int, bottom: Int) = |
| this.then( |
| BorderModifier( |
| start, top, end, bottom, debugInspectorInfo { |
| name = "border" |
| properties["start"] = start |
| } |
| )) |
| |
| private class BorderModifier( |
| val start: Int, |
| val top: Int, |
| val end: Int, |
| bottom: Int, |
| inspectorInfo: InspectorInfo.() -> Unit |
| ): Modifier.Element, InspectorValueInfo(inspectorInfo) { |
| } |
| |
| """ |
| ).indented() |
| ) |
| .run() |
| .expect( |
| """ |
| src/androidx/compose/ui/BorderModifier.kt:11: Error: These lambda arguments are missing in the InspectorInfo: bottom, end, top [ModifierInspectorInfo] |
| start, top, end, bottom, debugInspectorInfo { |
| ^ |
| 1 errors, 0 warnings |
| """ |
| ) |
| } |
| |
| @Test |
| fun inspectorInfoWithMissingDataClassMemberValues() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| fun Modifier.border(values: Borders) = |
| this.then(BorderModifier(values, inspectorInfo = debugInspectorInfo { |
| name = "border" |
| value = values.start |
| properties["top"] = values.top |
| })) |
| |
| private class BorderModifier( |
| val values: Borders, |
| inspectorInfo: InspectorInfo.() -> Unit |
| ): Modifier.Element, InspectorValueInfo(inspectorInfo) { |
| } |
| |
| data class Borders(val start: Int, val top: Int, val end: Int, val bottom: Int) { |
| constructor(all: Int) : this(all, all, all, all) |
| } |
| |
| """ |
| ).indented() |
| ) |
| .run() |
| .expect( |
| """ |
| src/androidx/compose/ui/BorderModifier.kt:9: Error: These lambda arguments are missing in the InspectorInfo: bottom, end [ModifierInspectorInfo] |
| this.then(BorderModifier(values, inspectorInfo = debugInspectorInfo { |
| ^ |
| 1 errors, 0 warnings |
| """ |
| ) |
| } |
| |
| @Test |
| fun missingInfoInConditionals() { |
| lint().files( |
| kotlin(Stubs.Modifier), |
| composedStub, |
| inspectableInfoStub, |
| kotlin( |
| """ |
| package androidx.compose.ui |
| |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.InspectorInfo |
| import androidx.compose.ui.platform.InspectorValueInfo |
| import androidx.compose.ui.platform.debugInspectorInfo |
| |
| class Brush |
| |
| class SolidColor(val color: Int): Brush() |
| |
| fun Modifier.border(width: Int): Modifier = composed( |
| inspectorInfo = debugInspectorInfo { |
| name = "border" |
| value = width |
| } |
| ) { |
| BorderModifier(shape, width, brush) |
| } |
| |
| fun Modifier.border2(width: Int): Modifier = |
| if (width > 0) { |
| border(width) |
| } else { |
| composed { BorderModifier(shape, width, brush) } |
| } |
| |
| fun Modifier.border3(width: Int): Modifier = |
| when { |
| width < 0 -> this |
| width < 2 -> border(width) |
| width < 3 -> composed { BorderModifier(shape, width, brush) } |
| else -> this |
| } |
| |
| fun Modifier.border4(width: Int): Modifier = |
| when { |
| width < 0 -> this |
| width < 2 -> border(width) |
| else -> this.then(BorderModifier(shape, width, brush)) |
| } |
| |
| private class BorderModifier(shape: Shape, width: Int, brush: Brush): Modifier |
| |
| """ |
| ).indented() |
| ) |
| .run() |
| .expect( |
| """ |
| src/androidx/compose/ui/Brush.kt:25: Error: Modifier missing inspectorInfo [ModifierInspectorInfo] |
| composed { BorderModifier(shape, width, brush) } |
| ~~~~~~~~ |
| src/androidx/compose/ui/Brush.kt:32: Error: Modifier missing inspectorInfo [ModifierInspectorInfo] |
| width < 3 -> composed { BorderModifier(shape, width, brush) } |
| ~~~~~~~~ |
| src/androidx/compose/ui/Brush.kt:40: Error: Modifier missing inspectorInfo [ModifierInspectorInfo] |
| else -> this.then(BorderModifier(shape, width, brush)) |
| ~~~~~~~~~~~~~~ |
| 3 errors, 0 warnings |
| """ |
| ) |
| } |
| } |