All Downloads are FREE. Search and download functionalities are using the official Maven repository.

androidTest.androidx.compose.ui.text.TextStyleLayoutAttributesTest.kt Maven / Gradle / Ivy

/*
 * Copyright 2022 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.
 */

package androidx.compose.ui.text

import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontSynthesis
import androidx.compose.ui.text.intl.LocaleList
import androidx.compose.ui.text.style.BaselineShift
import androidx.compose.ui.text.style.LineBreak
import androidx.compose.ui.text.style.LineHeightStyle
import androidx.compose.ui.text.style.LineHeightStyle.Trim
import androidx.compose.ui.text.style.LineHeightStyle.Alignment
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextDirection
import androidx.compose.ui.text.style.TextGeometricTransform
import androidx.compose.ui.text.style.TextIndent
import androidx.compose.ui.unit.em
import androidx.compose.ui.unit.sp
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import kotlin.reflect.KProperty1
import kotlin.reflect.KType
import kotlin.reflect.full.memberProperties
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4

@RunWith(JUnit4::class)
class TextStyleLayoutAttributesTest {
    @Test
    fun returns_true_for_the_same_instance() {
        val style = TextStyle(lineHeight = 1.em)
        assertThat(
            style.hasSameLayoutAffectingAttributes(style)
        ).isTrue()
    }

    @Test
    fun returns_true_for_the_equal_instance() {
        val style = TextStyle(lineHeight = 1.em)
        assertThat(
            style.hasSameLayoutAffectingAttributes(
                style.copy()
            )
        ).isTrue()
    }

    @Test
    fun returns_true_for_color_change() {
        val style = TextStyle(color = Color.Red)
        assertThat(
            style.hasSameLayoutAffectingAttributes(
                TextStyle(color = Color.Green)
            )
        ).isTrue()
    }

    @OptIn(ExperimentalTextApi::class)
    @Test
    fun returns_true_for_color_to_brush_change() {
        val style = TextStyle(color = Color.Red)
        assertThat(
            style.hasSameLayoutAffectingAttributes(TextStyle(brush = SolidColor(Color.Green)))
        ).isTrue()
    }

    @OptIn(ExperimentalTextApi::class)
    @Test
    fun returns_true_for_brush_to_color_change() {
        val style = TextStyle(brush = SolidColor(Color.Green))
        assertThat(
            style.hasSameLayoutAffectingAttributes(TextStyle(color = Color.Red))
        ).isTrue()
    }

    @OptIn(ExperimentalTextApi::class)
    @Test
    fun returns_true_for_brush_solid_color_change() {
        val style = TextStyle(brush = SolidColor(Color.Red))
        style.copy()
        assertThat(
            style.hasSameLayoutAffectingAttributes(TextStyle(brush = SolidColor(Color.Green)))
        ).isTrue()
    }

    @OptIn(ExperimentalTextApi::class)
    @Test
    fun returns_true_for_brush_shader_change() {
        val style = TextStyle(brush = Brush.linearGradient(listOf(Color.Black, Color.White)))
        assertThat(
            style.hasSameLayoutAffectingAttributes(TextStyle(
                brush = Brush.linearGradient(listOf(Color.Red, Color.Blue))
            ))
        ).isTrue()
    }

    @OptIn(ExperimentalTextApi::class)
    @Test
    fun returns_true_for_brush_alpha_change() {
        val brush = Brush.linearGradient(listOf(Color.Black, Color.White))
        val style = TextStyle(brush = brush, alpha = 0.5f)
        assertThat(
            style.hasSameLayoutAffectingAttributes(TextStyle(brush = brush, alpha = 0.7f))
        ).isTrue()
    }

    @Test
    fun returns_true_for_shadow_change() {
        val style = TextStyle(shadow = Shadow(color = Color.Red))
        assertThat(
            style.hasSameLayoutAffectingAttributes(
                TextStyle(shadow = Shadow(color = Color.Green))
            )
        ).isTrue()
    }

    @Test
    fun returns_true_for_textDecoration_change() {
        val style = TextStyle(textDecoration = TextDecoration.LineThrough)
        assertThat(
            style.hasSameLayoutAffectingAttributes(
                TextStyle(textDecoration = TextDecoration.Underline)
            )
        ).isTrue()
    }

    @Test
    fun returns_false_for_background_change() {
        // even though background does not change metrics, without recreating layout background
        // color animations doesn't work, do not remove.
        val style = TextStyle(background = Color.Red)
        assertThat(
            style.hasSameLayoutAffectingAttributes(
                TextStyle(background = Color.Green)
            )
        ).isFalse()
    }

    @Test
    fun returns_false_for_fontSize_change() {
        val style = TextStyle(fontSize = 10.sp)
        assertThat(
            style.hasSameLayoutAffectingAttributes(
                TextStyle(fontSize = 11.sp)
            )
        ).isFalse()
    }

    @Test
    fun returns_false_for_fontStyle_change() {
        val style = TextStyle(fontStyle = FontStyle.Italic)
        assertThat(
            style.hasSameLayoutAffectingAttributes(
                TextStyle(fontStyle = FontStyle.Normal)
            )
        ).isFalse()
    }

    @Test
    fun returns_false_for_fontSynthesis_change() {
        val style = TextStyle(fontSynthesis = FontSynthesis.Style)
        assertThat(
            style.hasSameLayoutAffectingAttributes(
                TextStyle(fontSynthesis = FontSynthesis.Weight)
            )
        ).isFalse()
    }

    @Test
    fun returns_false_for_fontFamily_change() {
        val style = TextStyle(fontFamily = FontFamily.SansSerif)
        assertThat(
            style.hasSameLayoutAffectingAttributes(
                TextStyle(fontFamily = FontFamily.Serif)
            )
        ).isFalse()
    }

    @Test
    fun returns_false_for_fontFeatureSettings_change() {
        val style = TextStyle(fontFeatureSettings = "abc")
        assertThat(
            style.hasSameLayoutAffectingAttributes(
                TextStyle(fontFeatureSettings = "def")
            )
        ).isFalse()
    }

    @Test
    fun returns_false_for_letterSpacing_change() {
        val style = TextStyle(letterSpacing = 0.2.sp)
        assertThat(
            style.hasSameLayoutAffectingAttributes(
                TextStyle(letterSpacing = 0.3.sp)
            )
        ).isFalse()
    }

    @Test
    fun returns_false_for_baselineShift_change() {
        val style = TextStyle(baselineShift = BaselineShift.Superscript)
        assertThat(
            style.hasSameLayoutAffectingAttributes(
                TextStyle(baselineShift = BaselineShift.Subscript)
            )
        ).isFalse()
    }

    @Test
    fun returns_false_for_textGeometricTransform_change() {
        val style = TextStyle(textGeometricTransform = TextGeometricTransform(scaleX = 1f))
        assertThat(
            style.hasSameLayoutAffectingAttributes(
                TextStyle(textGeometricTransform = TextGeometricTransform(scaleX = 2f))
            )
        ).isFalse()
    }

    @Test
    fun returns_false_for_localeList_change() {
        val style = TextStyle(localeList = LocaleList("en-US"))
        assertThat(
            style.hasSameLayoutAffectingAttributes(
                TextStyle(localeList = LocaleList("en-CA"))
            )
        ).isFalse()
    }

    @Test
    fun returns_false_for_textAlign_change() {
        val style = TextStyle(textAlign = TextAlign.Start)
        assertThat(
            style.hasSameLayoutAffectingAttributes(
                TextStyle(textAlign = TextAlign.End)
            )
        ).isFalse()
    }

    @Test
    fun returns_false_for_textDirection_change() {
        val style = TextStyle(textDirection = TextDirection.Ltr)
        assertThat(
            style.hasSameLayoutAffectingAttributes(
                TextStyle(textDirection = TextDirection.Rtl)
            )
        ).isFalse()
    }

    @Test
    fun returns_false_for_lineHeight_change() {
        val style = TextStyle(lineHeight = 1.em)
        assertThat(
            style.hasSameLayoutAffectingAttributes(
                TextStyle(lineHeight = 1.1.em)
            )
        ).isFalse()
    }

    @Test
    fun returns_false_for_textIndent_change() {
        val style = TextStyle(textIndent = TextIndent(firstLine = 0.sp))
        assertThat(
            style.hasSameLayoutAffectingAttributes(
                TextStyle(textIndent = TextIndent(firstLine = 1.sp))
            )
        ).isFalse()
    }

// TODO: `includeFontPadding` is not available on desktop. Comment out when `includeFontPadding` is removed in upstream.
// keep this comment in JB fork.
//    @Suppress("DEPRECATION")
//    @OptIn(ExperimentalTextApi::class)
//    @Test
//    fun returns_false_for_platformStyle_change() {
//        val style = TextStyle(platformStyle = PlatformTextStyle(includeFontPadding = false))
//        assertThat(
//            style.hasSameLayoutAffectingAttributes(
//                TextStyle(platformStyle = PlatformTextStyle(includeFontPadding = true))
//            )
//        ).isFalse()
//    }

    @Test
    fun returns_false_for_color_and_textAlign_change() {
        val style = TextStyle(color = Color.Red, textAlign = TextAlign.Start)
        assertThat(
            style.hasSameLayoutAffectingAttributes(
                TextStyle(color = Color.Blue, textAlign = TextAlign.End)
            )
        ).isFalse()
    }

    @OptIn(ExperimentalTextApi::class)
    @Test
    fun returns_false_for_lineHeightStyle_change() {
        val style = TextStyle(
            lineHeightStyle = LineHeightStyle(
                alignment = Alignment.Center,
                trim = Trim.None
            )
        )
        assertThat(
            style.hasSameLayoutAffectingAttributes(
                TextStyle(
                    lineHeightStyle = LineHeightStyle(
                        alignment = Alignment.Bottom,
                        trim = Trim.Both
                    )
                )
            )
        ).isFalse()
    }

    @OptIn(ExperimentalTextApi::class)
    @Test
    fun returns_false_for_lineBreak_change() {
        val style = TextStyle(
            lineBreak = LineBreak.Heading
        )
        assertThat(
            style.hasSameLayoutAffectingAttributes(
                TextStyle(
                    lineBreak = LineBreak.Paragraph
                )
            )
        ).isFalse()
    }

    @Test
    fun should_be_updated_when_a_new_attribute_is_added_to_TextStyle() {
        // TextLayoutHelper TextStyle.hasSameLayoutAffectingAttributes is very easy to forget
        // to update when TextStyle changes. Adding this test to fail so that when a new attribute
        // is added to TextStyle it will remind us that we need to update the function.
        val knownProperties = listOf(
            getProperty("color"),
            getProperty("brush"),
            getProperty("alpha"),
            getProperty("shadow"),
            getProperty("textDecoration"),
            getProperty("fontSize"),
            getProperty("fontWeight"),
            getProperty("fontStyle"),
            getProperty("fontSynthesis"),
            getProperty("fontFamily"),
            getProperty("fontFeatureSettings"),
            getProperty("letterSpacing"),
            getProperty("baselineShift"),
            getProperty("textGeometricTransform"),
            getProperty("localeList"),
            getProperty("background"),
            getProperty("textAlign"),
            getProperty("textDirection"),
            getProperty("lineHeight"),
            getProperty("textIndent"),
            getProperty("platformStyle"),
            // ParagraphStyle and SpanStyle properties are already compared, TextStyle should have
            // paragraph style attributes is tested in:
            // ui-text/../androidx/compose/ui/text/TextSpanParagraphStyleTest.kt
            getProperty("paragraphStyle"),
            getProperty("spanStyle"),
            getProperty("lineHeightStyle"),
            getProperty("hyphens"),
            getProperty("lineBreak")
        )

        val textStyleProperties = TextStyle::class.memberProperties.map { Property(it) }

        assertWithMessage(
            "New property is added to TextStyle, TextStyle.hasSameLayoutAffectingAttributes " +
                "should be updated accordingly"
        ).that(knownProperties).containsAtLeastElementsIn(textStyleProperties)
    }

    private fun getProperty(name: String): Property {
        return TextStyle::class.memberProperties.first { it.name == name }.let { Property(it) }
    }

    private data class Property(
        val name: String?,
        val type: KType
    ) {
        constructor(parameter: KProperty1<*, *>) : this(parameter.name, parameter.returnType)
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy