Unit tests for Box, Filler, Spacer, Row and Column modifiers (#270)

This commit is contained in:
EpicDima
2024-08-18 07:02:46 +03:00
committed by GitHub
parent b15c83435a
commit d51a959b79
5 changed files with 1177 additions and 0 deletions

View File

@ -19,6 +19,8 @@ import com.jakewharton.mosaic.ui.unit.Constraints
import com.jakewharton.mosaic.ui.unit.IntOffset
import com.jakewharton.mosaic.ui.unit.IntSize
import com.jakewharton.mosaic.ui.unit.constrain
import com.jakewharton.mosaic.ui.unit.constrainHeight
import com.jakewharton.mosaic.ui.unit.constrainWidth
import kotlin.math.max
const val s = " "
@ -156,3 +158,65 @@ fun testIntrinsics(
}
}
}
@Composable
internal fun ConstrainedBox(
constraints: Constraints,
modifier: Modifier = Modifier,
content: @Composable () -> Unit = {},
) {
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
val measurePolicy = object : MeasurePolicy {
override fun MeasureScope.measure(
measurables: List<Measurable>,
incomingConstraints: Constraints,
): MeasureResult {
val measurable = measurables.firstOrNull()
val childConstraints = incomingConstraints.constrain(constraints)
val placeable = measurable?.measure(childConstraints)
val layoutWidth = placeable?.width ?: childConstraints.minWidth
val layoutHeight = placeable?.height ?: childConstraints.minHeight
return layout(layoutWidth, layoutHeight) {
placeable?.place(0, 0)
}
}
override fun minIntrinsicWidth(
measurables: List<IntrinsicMeasurable>,
height: Int,
): Int {
val width = measurables.firstOrNull()?.minIntrinsicWidth(height) ?: 0
return constraints.constrainWidth(width)
}
override fun minIntrinsicHeight(
measurables: List<IntrinsicMeasurable>,
width: Int,
): Int {
val height = measurables.firstOrNull()?.minIntrinsicHeight(width) ?: 0
return constraints.constrainHeight(height)
}
override fun maxIntrinsicWidth(
measurables: List<IntrinsicMeasurable>,
height: Int,
): Int {
val width = measurables.firstOrNull()?.maxIntrinsicWidth(height) ?: 0
return constraints.constrainWidth(width)
}
override fun maxIntrinsicHeight(
measurables: List<IntrinsicMeasurable>,
width: Int,
): Int {
val height = measurables.firstOrNull()?.maxIntrinsicHeight(width) ?: 0
return constraints.constrainHeight(height)
}
}
Layout(
content = content,
modifier = modifier,
measurePolicy = measurePolicy,
)
}

View File

@ -0,0 +1,544 @@
package com.jakewharton.mosaic.ui
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import assertk.assertThat
import assertk.assertions.isEqualTo
import com.jakewharton.mosaic.AnsiRendering
import com.jakewharton.mosaic.ConstrainedBox
import com.jakewharton.mosaic.Container
import com.jakewharton.mosaic.TestChar
import com.jakewharton.mosaic.TestFiller
import com.jakewharton.mosaic.layout.MeasurePolicy
import com.jakewharton.mosaic.layout.aspectRatio
import com.jakewharton.mosaic.layout.fillMaxSize
import com.jakewharton.mosaic.layout.padding
import com.jakewharton.mosaic.layout.requiredSize
import com.jakewharton.mosaic.layout.requiredWidthIn
import com.jakewharton.mosaic.layout.size
import com.jakewharton.mosaic.layout.width
import com.jakewharton.mosaic.modifier.Modifier
import com.jakewharton.mosaic.mosaicNodes
import com.jakewharton.mosaic.mosaicNodesWithMeasureAndPlace
import com.jakewharton.mosaic.position
import com.jakewharton.mosaic.renderMosaic
import com.jakewharton.mosaic.replaceLineEndingsWithCRLF
import com.jakewharton.mosaic.s
import com.jakewharton.mosaic.size
import com.jakewharton.mosaic.testIntrinsics
import com.jakewharton.mosaic.ui.unit.Constraints
import com.jakewharton.mosaic.ui.unit.IntOffset
import com.jakewharton.mosaic.ui.unit.IntSize
import kotlin.test.Test
class BoxTest {
@Test fun boxWithAlignedAndPositionedChildren() {
val size = 6
val content = @Composable {
Container(alignment = Alignment.TopStart) {
Box {
Container(
modifier = Modifier.align(Alignment.BottomEnd),
width = size,
height = size,
)
TestFiller(modifier = Modifier.matchParentSize().padding(2))
}
}
}
val rootNode = mosaicNodesWithMeasureAndPlace(content)
val actual = renderMosaic(content)
val boxNode = rootNode.children[0].children[0]
val alignedChildContainerNode = boxNode.children[0]
val positionedChildContainerNode = boxNode.children[1]
assertThat(alignedChildContainerNode.size).isEqualTo(IntSize(size, size))
assertThat(alignedChildContainerNode.position).isEqualTo(IntOffset.Zero)
assertThat(positionedChildContainerNode.size).isEqualTo(IntSize(size, size))
assertThat(positionedChildContainerNode.position).isEqualTo(IntOffset.Zero)
assertThat(actual).isEqualTo(
"""
| $s
| $s
| $TestChar$TestChar $s
| $TestChar$TestChar $s
| $s
| $s
|
""".trimMargin().replaceLineEndingsWithCRLF(),
)
}
@Test fun boxWithMultipleAlignedChildren() {
val size = 200
val doubleSize = size * 2
val rootNode = mosaicNodesWithMeasureAndPlace {
Container(alignment = Alignment.TopStart) {
Box {
Container(
modifier = Modifier.align(Alignment.BottomEnd),
width = size,
height = size,
)
Container(
modifier = Modifier.align(Alignment.BottomEnd),
width = doubleSize,
height = doubleSize,
)
}
}
}
val boxNode = rootNode.children[0].children[0]
val firstChildContainerNode = boxNode.children[0]
val secondChildContainerNode = boxNode.children[1]
assertThat(boxNode.size).isEqualTo(IntSize(doubleSize, doubleSize))
assertThat(firstChildContainerNode.size).isEqualTo(IntSize(size, size))
assertThat(firstChildContainerNode.position).isEqualTo(IntOffset(size, size))
assertThat(secondChildContainerNode.size).isEqualTo(IntSize(doubleSize, doubleSize))
assertThat(secondChildContainerNode.position).isEqualTo(IntOffset.Zero)
}
@Test fun boxWithStretchChildrenPaddingLeftTop() {
val size = 6
val halfSize = size / 2
val inset = 1
val content = @Composable {
Container(alignment = Alignment.TopStart) {
Box {
Container(
modifier = Modifier.align(Alignment.Center),
width = size,
height = size,
)
TestFiller(
modifier = Modifier.matchParentSize().padding(left = inset, top = inset).size(halfSize),
)
}
}
}
val rootNode = mosaicNodesWithMeasureAndPlace(content)
val actual = renderMosaic(content)
val boxNode = rootNode.children[0].children[0]
val firstChildContainerNode = boxNode.children[0]
val secondChildContainerNode = boxNode.children[1]
assertThat(boxNode.size).isEqualTo(IntSize(size, size))
assertThat(firstChildContainerNode.size).isEqualTo(IntSize(size, size))
assertThat(firstChildContainerNode.position).isEqualTo(IntOffset.Zero)
assertThat(secondChildContainerNode.size).isEqualTo(IntSize(size, size))
assertThat(secondChildContainerNode.position).isEqualTo(IntOffset.Zero)
assertThat(actual).isEqualTo(
"""
| $s
| $TestChar$TestChar$TestChar$TestChar$TestChar
| $TestChar$TestChar$TestChar$TestChar$TestChar
| $TestChar$TestChar$TestChar$TestChar$TestChar
| $TestChar$TestChar$TestChar$TestChar$TestChar
| $TestChar$TestChar$TestChar$TestChar$TestChar
|
""".trimMargin().replaceLineEndingsWithCRLF(),
)
}
@Test fun boxWithStretchChildrenPaddingRightBottom() {
val size = 6
val halfSize = size / 2
val inset = 1
val content = @Composable {
Container(alignment = Alignment.TopStart) {
Box {
Container(
modifier = Modifier.align(Alignment.Center),
width = size,
height = size,
)
TestFiller(
modifier = Modifier.matchParentSize().padding(right = inset, bottom = inset)
.size(halfSize),
)
}
}
}
val rootNode = mosaicNodesWithMeasureAndPlace(content)
val actual = renderMosaic(content)
val boxNode = rootNode.children[0].children[0]
val firstChildContainerNode = boxNode.children[0]
val secondChildContainerNode = boxNode.children[1]
assertThat(boxNode.size).isEqualTo(IntSize(size, size))
assertThat(firstChildContainerNode.size).isEqualTo(IntSize(size, size))
assertThat(firstChildContainerNode.position).isEqualTo(IntOffset.Zero)
assertThat(secondChildContainerNode.size).isEqualTo(IntSize(size, size))
assertThat(secondChildContainerNode.position).isEqualTo(IntOffset.Zero)
assertThat(actual).isEqualTo(
"""
|$TestChar$TestChar$TestChar$TestChar$TestChar$s
|$TestChar$TestChar$TestChar$TestChar$TestChar$s
|$TestChar$TestChar$TestChar$TestChar$TestChar$s
|$TestChar$TestChar$TestChar$TestChar$TestChar$s
|$TestChar$TestChar$TestChar$TestChar$TestChar$s
| $s
|
""".trimMargin().replaceLineEndingsWithCRLF(),
)
}
@Test fun boxWithStretchChildrenPaddingLeftRight() {
val size = 6
val halfSize = size / 2
val inset = 1
val content = @Composable {
Container(alignment = Alignment.TopStart) {
Box {
Container(
modifier = Modifier.align(Alignment.Center),
width = size,
height = size,
)
TestFiller(
modifier = Modifier.matchParentSize().padding(left = inset, right = inset)
.size(halfSize),
)
}
}
}
val rootNode = mosaicNodesWithMeasureAndPlace(content)
val actual = renderMosaic(content)
val boxNode = rootNode.children[0].children[0]
val firstChildContainerNode = boxNode.children[0]
val secondChildContainerNode = boxNode.children[1]
assertThat(boxNode.size).isEqualTo(IntSize(size, size))
assertThat(firstChildContainerNode.size).isEqualTo(IntSize(size, size))
assertThat(firstChildContainerNode.position).isEqualTo(IntOffset.Zero)
assertThat(secondChildContainerNode.size).isEqualTo(IntSize(size, size))
assertThat(secondChildContainerNode.position).isEqualTo(IntOffset.Zero)
assertThat(actual).isEqualTo(
"""
| $TestChar$TestChar$TestChar$TestChar$s
| $TestChar$TestChar$TestChar$TestChar$s
| $TestChar$TestChar$TestChar$TestChar$s
| $TestChar$TestChar$TestChar$TestChar$s
| $TestChar$TestChar$TestChar$TestChar$s
| $TestChar$TestChar$TestChar$TestChar$s
|
""".trimMargin().replaceLineEndingsWithCRLF(),
)
}
@Test fun boxWithStretchChildrenPaddingTopBottom() {
val size = 6
val halfSize = size / 2
val inset = 1
val content = @Composable {
Container(alignment = Alignment.TopStart) {
Box {
Container(
modifier = Modifier.align(Alignment.Center),
width = size,
height = size,
)
TestFiller(
modifier = Modifier.matchParentSize().padding(top = inset, bottom = inset)
.size(halfSize),
)
}
}
}
val rootNode = mosaicNodesWithMeasureAndPlace(content)
val actual = renderMosaic(content)
val boxNode = rootNode.children[0].children[0]
val firstChildContainerNode = boxNode.children[0]
val secondChildContainerNode = boxNode.children[1]
assertThat(boxNode.size).isEqualTo(IntSize(size, size))
assertThat(firstChildContainerNode.size).isEqualTo(IntSize(size, size))
assertThat(firstChildContainerNode.position).isEqualTo(IntOffset.Zero)
assertThat(secondChildContainerNode.size).isEqualTo(IntSize(size, size))
assertThat(secondChildContainerNode.position).isEqualTo(IntOffset.Zero)
assertThat(actual).isEqualTo(
"""
| $s
|$TestChar$TestChar$TestChar$TestChar$TestChar$TestChar
|$TestChar$TestChar$TestChar$TestChar$TestChar$TestChar
|$TestChar$TestChar$TestChar$TestChar$TestChar$TestChar
|$TestChar$TestChar$TestChar$TestChar$TestChar$TestChar
| $s
|
""".trimMargin().replaceLineEndingsWithCRLF(),
)
}
@Test fun boxExpanded() {
val size = 250
val halfSize = 125
val rootNode = mosaicNodesWithMeasureAndPlace {
Container(alignment = Alignment.TopStart) {
Container(modifier = Modifier.size(size)) {
Box {
Container(modifier = Modifier.fillMaxSize())
Container(
modifier = Modifier.align(Alignment.BottomEnd),
width = halfSize,
height = halfSize,
)
}
}
}
}
val outerContainerNode = rootNode.children[0].children[0]
val boxNode = outerContainerNode.children[0]
val firstChildContainerNode = boxNode.children[0]
val secondChildContainerNode = boxNode.children[1]
assertThat(outerContainerNode.size).isEqualTo(IntSize(size, size))
assertThat(firstChildContainerNode.size).isEqualTo(IntSize(size, size))
assertThat(firstChildContainerNode.position).isEqualTo(IntOffset.Zero)
assertThat(secondChildContainerNode.size).isEqualTo(IntSize(halfSize, halfSize))
assertThat(secondChildContainerNode.position).isEqualTo(
IntOffset(
size - halfSize,
size - halfSize,
),
)
}
@Test fun boxAlignmentParameter() {
val outerSize = 50
val innerSize = 10
val rootNode = mosaicNodesWithMeasureAndPlace {
Box(
contentAlignment = Alignment.BottomEnd,
modifier = Modifier.requiredSize(outerSize),
) {
Box(Modifier.requiredSize(innerSize))
}
}
val innerBoxNode = rootNode.children[0].children[0]
assertThat(innerBoxNode.position)
.isEqualTo(IntOffset(outerSize - innerSize, outerSize - innerSize))
}
@Test fun boxOutermostGravityWins() {
val size = 10
val rootNode = mosaicNodesWithMeasureAndPlace {
Box(Modifier.requiredSize(size)) {
Box(Modifier.align(Alignment.BottomEnd).align(Alignment.TopStart))
}
}
val innerBoxNode = rootNode.children[0].children[0]
assertThat(innerBoxNode.position).isEqualTo(IntOffset(size, size))
}
@Test fun boxChildAffectsBoxSize() {
val size = mutableIntStateOf(10)
var measure = 0
var layout = 0
val rootNode = mosaicNodes {
Box {
Layout(
content = {
Box {
Box(
Modifier.requiredSize(size.value, 10),
)
}
},
measurePolicy = remember {
MeasurePolicy { measurables, constraints ->
val placeable = measurables.first().measure(constraints)
++measure
layout(placeable.width, placeable.height) {
placeable.place(0, 0)
++layout
}
}
},
)
}
}
val rendering = AnsiRendering()
rendering.render(rootNode)
assertThat(measure).isEqualTo(1)
assertThat(layout).isEqualTo(1)
size.value = 20
rendering.render(rootNode)
assertThat(measure).isEqualTo(2)
assertThat(layout).isEqualTo(2)
}
@Test fun boxCanPropagateMinConstraints() {
val rootNode = mosaicNodesWithMeasureAndPlace {
Box(
modifier = Modifier.requiredWidthIn(20, 40),
propagateMinConstraints = true,
) {
Box(modifier = Modifier.width(10))
}
}
val innerBoxNode = rootNode.children[0].children[0]
assertThat(innerBoxNode.width).isEqualTo(20)
}
@Test fun boxTracksPropagateMinConstraintsChanges() {
val pmc = mutableStateOf(true)
val content = @Composable {
Box(
modifier = Modifier.requiredWidthIn(20, 40),
propagateMinConstraints = pmc.value,
contentAlignment = Alignment.Center,
) {
Box(modifier = Modifier.width(10))
}
}
val firstRootNode = mosaicNodesWithMeasureAndPlace(content)
assertThat(firstRootNode.children[0].children[0].width).isEqualTo(20)
pmc.value = false
val secondRootNode = mosaicNodesWithMeasureAndPlace(content)
assertThat(secondRootNode.children[0].children[0].width).isEqualTo(10)
}
@Test fun boxHasCorrectIntrinsicMeasurements() {
val testWidth = 90
val testHeight = 80
val testDimension = 200
// When measuring the height with testDimension, width should be double
val expectedWidth = testDimension * 2
// When measuring the width with testDimension, height should be half
val expectedHeight = testDimension / 2
testIntrinsics(
@Composable {
Box {
Container(modifier = Modifier.align(Alignment.TopStart).aspectRatio(2f))
ConstrainedBox(
constraints = Constraints.fixed(testWidth, testHeight),
modifier = Modifier.align(Alignment.BottomCenter),
)
ConstrainedBox(
constraints = Constraints.fixed(200, 200),
modifier = Modifier.matchParentSize().padding(10),
)
}
},
) { minIntrinsicWidth, minIntrinsicHeight, maxIntrinsicWidth, maxIntrinsicHeight ->
// Min width
assertThat(minIntrinsicWidth(0)).isEqualTo(testWidth)
assertThat(minIntrinsicWidth(testDimension)).isEqualTo(expectedWidth)
assertThat(minIntrinsicWidth(Constraints.Infinity)).isEqualTo(testWidth)
// Min height
assertThat(minIntrinsicHeight(0)).isEqualTo(testHeight)
assertThat(minIntrinsicHeight(testDimension)).isEqualTo(expectedHeight)
assertThat(minIntrinsicHeight(Constraints.Infinity)).isEqualTo(testHeight)
// Max width
assertThat(maxIntrinsicWidth(0)).isEqualTo(testWidth)
assertThat(maxIntrinsicWidth(testDimension)).isEqualTo(expectedWidth)
assertThat(maxIntrinsicWidth(Constraints.Infinity)).isEqualTo(testWidth)
// Max height
assertThat(maxIntrinsicHeight(0)).isEqualTo(testHeight)
assertThat(maxIntrinsicHeight(testDimension)).isEqualTo(expectedHeight)
assertThat(maxIntrinsicHeight(Constraints.Infinity)).isEqualTo(testHeight)
}
}
@Test fun boxHasCorrectIntrinsicMeasurementsWithNoAlignedChildren() {
testIntrinsics(
@Composable {
Box {
ConstrainedBox(
modifier = Modifier.matchParentSize().padding(10),
constraints = Constraints.fixed(200, 200),
)
}
},
) { minIntrinsicWidth, minIntrinsicHeight, maxIntrinsicWidth, maxIntrinsicHeight ->
// Min width
assertThat(minIntrinsicWidth(50)).isEqualTo(0)
assertThat(minIntrinsicWidth(Constraints.Infinity)).isEqualTo(0)
// Min height
assertThat(minIntrinsicHeight(50)).isEqualTo(0)
assertThat(minIntrinsicHeight(Constraints.Infinity)).isEqualTo(0)
// Max width
assertThat(maxIntrinsicWidth(50)).isEqualTo(0)
assertThat(maxIntrinsicWidth(Constraints.Infinity)).isEqualTo(0)
// Max height
assertThat(maxIntrinsicHeight(50)).isEqualTo(0)
assertThat(maxIntrinsicHeight(Constraints.Infinity)).isEqualTo(0)
}
}
@Test fun boxSimpleDebug() {
val actual = mosaicNodes {
Box()
}
assertThat(actual.toString()).isEqualTo(
"""
|Box() x=0 y=0 w=0 h=0
""".trimMargin(),
)
}
@Test fun boxDebug() {
val actual = mosaicNodes {
Box(contentAlignment = Alignment.BottomCenter, propagateMinConstraints = true) {}
}
assertThat(actual.toString()).isEqualTo(
"""
|Box(alignment=Alignment(horizontalBias=0, verticalBias=1), propagateMinConstraints=true) x=0 y=0 w=0 h=0
""".trimMargin(),
)
}
}

View File

@ -0,0 +1,189 @@
package com.jakewharton.mosaic.ui
import assertk.assertThat
import assertk.assertions.isEqualTo
import com.jakewharton.mosaic.Container
import com.jakewharton.mosaic.TestChar
import com.jakewharton.mosaic.TestFiller
import com.jakewharton.mosaic.layout.height
import com.jakewharton.mosaic.layout.padding
import com.jakewharton.mosaic.layout.size
import com.jakewharton.mosaic.layout.width
import com.jakewharton.mosaic.modifier.Modifier
import com.jakewharton.mosaic.mosaicNodes
import com.jakewharton.mosaic.mosaicNodesWithMeasureAndPlace
import com.jakewharton.mosaic.renderMosaic
import com.jakewharton.mosaic.replaceLineEndingsWithCRLF
import com.jakewharton.mosaic.s
import com.jakewharton.mosaic.size
import com.jakewharton.mosaic.ui.unit.Constraints
import com.jakewharton.mosaic.ui.unit.IntSize
import kotlin.test.Test
class FillerTest {
private val bigConstraints = Constraints(maxWidth = 5000, maxHeight = 5000)
@Test fun fillerFixed() {
val width = 4
val height = 6
val actual = renderMosaic {
TestFiller(Modifier.size(width = width, height = height))
}
assertThat(actual).isEqualTo(
"""
|$TestChar$TestChar$TestChar$TestChar
|$TestChar$TestChar$TestChar$TestChar
|$TestChar$TestChar$TestChar$TestChar
|$TestChar$TestChar$TestChar$TestChar
|$TestChar$TestChar$TestChar$TestChar
|$TestChar$TestChar$TestChar$TestChar
|
""".trimMargin().replaceLineEndingsWithCRLF(),
)
}
@Test fun fillerFixedWithPadding() {
val width = 4
val height = 6
val actual = renderMosaic {
TestFiller(Modifier.size(width = width, height = height).padding(1))
}
assertThat(actual).isEqualTo(
"""
| $s
| $TestChar$TestChar$s
| $TestChar$TestChar$s
| $TestChar$TestChar$s
| $TestChar$TestChar$s
| $s
|
""".trimMargin().replaceLineEndingsWithCRLF(),
)
}
@Test fun fillerFixedSize() {
val width = 40
val height = 71
val rootNode = mosaicNodesWithMeasureAndPlace {
Container(constraints = bigConstraints) {
TestFiller(Modifier.size(width = width, height = height))
}
}
val fillerNode = rootNode.children[0].children[0]
assertThat(fillerNode.size).isEqualTo(IntSize(width, height))
}
@Test fun fillerFixedWithSmallerContainer() {
val width = 40
val height = 71
val containerWidth = 5
val containerHeight = 7
val rootNode = mosaicNodesWithMeasureAndPlace {
Box {
Container(
constraints = Constraints(
maxWidth = containerWidth,
maxHeight = containerHeight,
),
) {
TestFiller(Modifier.size(width = width, height = height))
}
}
}
val fillerNode = rootNode.children[0].children[0].children[0]
assertThat(fillerNode.size).isEqualTo(IntSize(containerWidth, containerHeight))
}
@Test fun fillerWidth() {
val width = 71
val rootNode = mosaicNodesWithMeasureAndPlace {
Container(constraints = bigConstraints) {
TestFiller(Modifier.width(width))
}
}
val fillerNode = rootNode.children[0].children[0]
assertThat(fillerNode.size).isEqualTo(IntSize(width, 0))
}
@Test fun fillerWidthWithSmallerContainer() {
val width = 40
val containerWidth = 5
val containerHeight = 7
val rootNode = mosaicNodesWithMeasureAndPlace {
Box {
Container(
constraints = Constraints(
maxWidth = containerWidth,
maxHeight = containerHeight,
),
) {
TestFiller(Modifier.width(width))
}
}
}
val fillerNode = rootNode.children[0].children[0].children[0]
assertThat(fillerNode.size).isEqualTo(IntSize(containerWidth, 0))
}
@Test fun fillerHeight() {
val height = 7
val rootNode = mosaicNodesWithMeasureAndPlace {
Container(constraints = bigConstraints) {
TestFiller(Modifier.height(height))
}
}
val fillerNode = rootNode.children[0].children[0]
assertThat(fillerNode.size).isEqualTo(IntSize(0, height))
}
@Test fun fillerHeightWithSmallerContainer() {
val height = 23
val containerWidth = 5
val containerHeight = 7
val rootNode = mosaicNodesWithMeasureAndPlace {
Box {
Container(
constraints = Constraints(
maxWidth = containerWidth,
maxHeight = containerHeight,
),
) {
TestFiller(Modifier.height(height))
}
}
}
val fillerNode = rootNode.children[0].children[0].children[0]
assertThat(fillerNode.size).isEqualTo(IntSize(0, containerHeight))
}
@Test fun fillerDebug() {
val actual = mosaicNodes {
TestFiller()
}
assertThat(actual.toString()).isEqualTo(
"""
|Filler('$TestChar') x=0 y=0 w=0 h=0 DrawBehind
""".trimMargin(),
)
}
}

View File

@ -0,0 +1,215 @@
package com.jakewharton.mosaic.ui
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import assertk.assertThat
import assertk.assertions.isEqualTo
import com.jakewharton.mosaic.layout.height
import com.jakewharton.mosaic.layout.size
import com.jakewharton.mosaic.layout.width
import com.jakewharton.mosaic.layout.wrapContentHeight
import com.jakewharton.mosaic.layout.wrapContentWidth
import com.jakewharton.mosaic.modifier.Modifier
import com.jakewharton.mosaic.mosaicNodesWithMeasureAndPlace
import kotlin.test.Test
class RowColumnModifierTest {
@Test fun rowUpdatesOnAlignmentChange() {
val count = 5
var alignment by mutableStateOf(Alignment.Top)
val content = @Composable {
Box(Modifier.size(100)) {
Row(Modifier.wrapContentHeight()) {
repeat(count) { index ->
Box(Modifier.width(20).height(if (index == count - 1) 10 else 20).align(alignment))
}
}
}
}
mosaicNodesWithMeasureAndPlace(content).let { rootNode ->
val fifthChildBoxNode = rootNode.children[0].children[0].children[count - 1]
assertThat(fifthChildBoxNode.y).isEqualTo(0)
}
alignment = Alignment.CenterVertically
mosaicNodesWithMeasureAndPlace(content).let { rootNode ->
val fifthChildBoxNode = rootNode.children[0].children[0].children[count - 1]
assertThat(fifthChildBoxNode.y).isEqualTo(5)
}
}
@Test fun rowUpdatesOnWeightChange() {
val count = 5
var fill by mutableStateOf(false)
val content = @Composable {
Box(Modifier.size(200)) {
Row(Modifier.wrapContentHeight()) {
repeat(count) {
Box(Modifier.size(20).weight(1f, fill))
}
}
}
}
mosaicNodesWithMeasureAndPlace(content).let { rootNode ->
repeat(count) { index ->
val childBoxNode = rootNode.children[0].children[0].children[index]
assertThat(childBoxNode.width).isEqualTo(20)
}
}
fill = true
mosaicNodesWithMeasureAndPlace(content).let { rootNode ->
repeat(count) { index ->
val childBoxNode = rootNode.children[0].children[0].children[index]
assertThat(childBoxNode.width).isEqualTo(40)
}
}
}
@Test fun rowUpdatesOnWeightAndAlignmentChange() {
val count = 5
var fill by mutableStateOf(false)
var alignment by mutableStateOf(Alignment.Top)
val content = @Composable {
Box(Modifier.size(200)) {
Row(Modifier.wrapContentHeight()) {
repeat(count) { index ->
Box(
Modifier.width(20).height(if (index == count - 1) 10 else 20)
.weight(1f, fill)
.align(alignment),
)
}
}
}
}
mosaicNodesWithMeasureAndPlace(content).let { rootNode ->
repeat(count) { index ->
val childBoxNode = rootNode.children[0].children[0].children[index]
assertThat(childBoxNode.width).isEqualTo(20)
}
val childBoxNode = rootNode.children[0].children[0].children[count - 1]
assertThat(childBoxNode.y).isEqualTo(0)
}
alignment = Alignment.CenterVertically
fill = true
mosaicNodesWithMeasureAndPlace(content).let { rootNode ->
repeat(count) { index ->
val childBoxNode = rootNode.children[0].children[0].children[index]
assertThat(childBoxNode.width).isEqualTo(40)
}
val childBoxNode = rootNode.children[0].children[0].children[count - 1]
assertThat(childBoxNode.y).isEqualTo(5)
}
}
@Test fun columnUpdatesOnAlignmentChange() {
val count = 5
var alignment by mutableStateOf(Alignment.Start)
val content = @Composable {
Box(Modifier.size(100)) {
Column(Modifier.wrapContentWidth().wrapContentHeight()) {
repeat(count) { index ->
Box(Modifier.height(20).width(if (index == count - 1) 10 else 20).align(alignment))
}
}
}
}
mosaicNodesWithMeasureAndPlace(content).let { rootNode ->
val fifthChildBoxNode = rootNode.children[0].children[0].children[count - 1]
assertThat(fifthChildBoxNode.x).isEqualTo(0)
}
alignment = Alignment.CenterHorizontally
mosaicNodesWithMeasureAndPlace(content).let { rootNode ->
val fifthChildBoxNode = rootNode.children[0].children[0].children[count - 1]
assertThat(fifthChildBoxNode.x).isEqualTo(5)
}
}
@Test fun columnUpdatesOnWeightChange() {
val count = 5
var fill by mutableStateOf(false)
val content = @Composable {
Box(Modifier.size(200)) {
Column(Modifier.wrapContentHeight()) {
repeat(count) {
Box(Modifier.size(20).weight(1f, fill))
}
}
}
}
mosaicNodesWithMeasureAndPlace(content).let { rootNode ->
repeat(count) { index ->
val childBoxNode = rootNode.children[0].children[0].children[index]
assertThat(childBoxNode.height).isEqualTo(20)
}
}
fill = true
mosaicNodesWithMeasureAndPlace(content).let { rootNode ->
repeat(count) { index ->
val childBoxNode = rootNode.children[0].children[0].children[index]
assertThat(childBoxNode.height).isEqualTo(40)
}
}
}
@Test fun columnUpdatesOnWeightAndAlignmentChange() {
val count = 5
var fill by mutableStateOf(false)
var alignment by mutableStateOf(Alignment.Start)
val content = @Composable {
Box(Modifier.size(200)) {
Column(Modifier.wrapContentHeight()) {
repeat(count) { index ->
Box(
Modifier.height(20).width(if (index == count - 1) 10 else 20).weight(1f, fill)
.align(alignment),
)
}
}
}
}
mosaicNodesWithMeasureAndPlace(content).let { rootNode ->
repeat(count) { index ->
val childBoxNode = rootNode.children[0].children[0].children[index]
assertThat(childBoxNode.height).isEqualTo(20)
}
val childBoxNode = rootNode.children[0].children[0].children[count - 1]
assertThat(childBoxNode.x).isEqualTo(0)
}
alignment = Alignment.CenterHorizontally
fill = true
mosaicNodesWithMeasureAndPlace(content).let { rootNode ->
repeat(count) { index ->
val childBoxNode = rootNode.children[0].children[0].children[index]
assertThat(childBoxNode.height).isEqualTo(40)
}
val childBoxNode = rootNode.children[0].children[0].children[count - 1]
assertThat(childBoxNode.x).isEqualTo(5)
}
}
}

View File

@ -0,0 +1,165 @@
package com.jakewharton.mosaic.ui
import assertk.assertThat
import assertk.assertions.isEqualTo
import com.jakewharton.mosaic.Container
import com.jakewharton.mosaic.layout.height
import com.jakewharton.mosaic.layout.size
import com.jakewharton.mosaic.layout.width
import com.jakewharton.mosaic.modifier.Modifier
import com.jakewharton.mosaic.mosaicNodes
import com.jakewharton.mosaic.mosaicNodesWithMeasureAndPlace
import com.jakewharton.mosaic.renderMosaic
import com.jakewharton.mosaic.replaceLineEndingsWithCRLF
import com.jakewharton.mosaic.s
import com.jakewharton.mosaic.size
import com.jakewharton.mosaic.ui.unit.Constraints
import com.jakewharton.mosaic.ui.unit.IntSize
import kotlin.test.Test
class SpacerTest {
private val bigConstraints = Constraints(maxWidth = 5000, maxHeight = 5000)
@Test fun spacerFixed() {
val width = 4
val height = 6
val actual = renderMosaic {
Spacer(Modifier.size(width = width, height = height))
}
assertThat(actual).isEqualTo(
"""
| $s
| $s
| $s
| $s
| $s
| $s
|
""".trimMargin().replaceLineEndingsWithCRLF(),
)
}
@Test fun spacerFixedSize() {
val width = 40
val height = 71
val rootNode = mosaicNodesWithMeasureAndPlace {
Container(constraints = bigConstraints) {
Spacer(Modifier.size(width = width, height = height))
}
}
val spacerNode = rootNode.children[0].children[0]
assertThat(spacerNode.size).isEqualTo(IntSize(width, height))
}
@Test fun spacerFixedWithSmallerContainer() {
val width = 40
val height = 71
val containerWidth = 5
val containerHeight = 7
val rootNode = mosaicNodesWithMeasureAndPlace {
Box {
Container(
constraints = Constraints(
maxWidth = containerWidth,
maxHeight = containerHeight,
),
) {
Spacer(Modifier.size(width = width, height = height))
}
}
}
val spacerNode = rootNode.children[0].children[0].children[0]
assertThat(spacerNode.size).isEqualTo(IntSize(containerWidth, containerHeight))
}
@Test fun spacerWidth() {
val width = 71
val rootNode = mosaicNodesWithMeasureAndPlace {
Container(constraints = bigConstraints) {
Spacer(Modifier.width(width))
}
}
val spacerNode = rootNode.children[0].children[0]
assertThat(spacerNode.size).isEqualTo(IntSize(width, 0))
}
@Test fun spacerWidthWithSmallerContainer() {
val width = 40
val containerWidth = 5
val containerHeight = 7
val rootNode = mosaicNodesWithMeasureAndPlace {
Box {
Container(
constraints = Constraints(
maxWidth = containerWidth,
maxHeight = containerHeight,
),
) {
Spacer(Modifier.width(width))
}
}
}
val spacerNode = rootNode.children[0].children[0].children[0]
assertThat(spacerNode.size).isEqualTo(IntSize(containerWidth, 0))
}
@Test fun spacerHeight() {
val height = 7
val rootNode = mosaicNodesWithMeasureAndPlace {
Container(constraints = bigConstraints) {
Spacer(Modifier.height(height))
}
}
val spacerNode = rootNode.children[0].children[0]
assertThat(spacerNode.size).isEqualTo(IntSize(0, height))
}
@Test fun spacerHeightWithSmallerContainer() {
val height = 23
val containerWidth = 5
val containerHeight = 7
val rootNode = mosaicNodesWithMeasureAndPlace {
Box {
Container(
constraints = Constraints(
maxWidth = containerWidth,
maxHeight = containerHeight,
),
) {
Spacer(Modifier.height(height))
}
}
}
val spacerNode = rootNode.children[0].children[0].children[0]
assertThat(spacerNode.size).isEqualTo(IntSize(0, containerHeight))
}
@Test fun spacerDebug() {
val actual = mosaicNodes {
Spacer()
}
assertThat(actual.toString()).isEqualTo(
"""
|Spacer() x=0 y=0 w=0 h=0
""".trimMargin(),
)
}
}