[go: nahoru, domu]

blob: a05328f72da228def4fee3890c13463dca761404 [file] [log] [blame]
/*
* 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.
*/
package androidx.ui.tooling.inspector
import android.view.View
import android.view.ViewGroup
import androidx.compose.InternalComposeApi
import androidx.compose.foundation.Text
import androidx.compose.foundation.layout.Column
import androidx.compose.resetSourceInfo
import androidx.test.filters.SmallTest
import androidx.ui.core.OwnedLayer
import androidx.compose.ui.graphics.Color
import androidx.ui.material.Button
import androidx.ui.material.Surface
import androidx.ui.tooling.Group
import androidx.ui.tooling.Inspectable
import androidx.ui.tooling.R
import androidx.ui.tooling.SlotTableRecord
import androidx.ui.tooling.ToolingTest
import androidx.ui.tooling.asTree
import androidx.ui.tooling.position
import androidx.ui.unit.Density
import androidx.ui.unit.Dp
import androidx.ui.unit.dp
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import kotlin.math.roundToInt
private const val DEBUG = false
@SmallTest
@RunWith(JUnit4::class)
class LayoutInspectorTreeTest : ToolingTest() {
private lateinit var density: Density
private lateinit var view: View
private lateinit var source: String
@Before
fun density() {
@OptIn(InternalComposeApi::class)
resetSourceInfo()
density = Density(activity)
view = activityTestRule.activity.findViewById<ViewGroup>(android.R.id.content)
}
@Ignore("Manual test")
@Test
fun buildTree() {
val slotTableRecord = SlotTableRecord.create()
show {
Inspectable(slotTableRecord) {
Column {
Text(text = "Hello World", color = Color.Green)
Surface {
Button(onClick = {}) { Text(text = "OK") }
}
}
}
}
// TODO: Find out if we can set "settings put global debug_view_attributes 1" in tests
view.setTag(R.id.inspection_slot_table_set, slotTableRecord.store)
val viewWidth = with(density) { view.width.toDp() }
val viewHeight = with(density) { view.height.toDp() }
dumpSlotTableSet(slotTableRecord)
val builder = LayoutInspectorTree()
val nodes = builder.convert(view)
dumpNodes(nodes)
val nodeIterator = nodes.flatMap { flatten(it) }.iterator()
fun validate(
isRenderNode: Boolean = false,
name: String,
fileName: String,
lineNumber: Int = -1,
left: Dp,
top: Dp,
width: Dp,
height: Dp,
children: List<String> = emptyList(),
block: ParameterValidationReceiver.() -> Unit = {}
) {
assertThat(nodeIterator.hasNext()).isTrue()
val node = nodeIterator.next()
if (isRenderNode) {
assertThat(node.id).isGreaterThan(0L)
} else {
assertThat(node.id).isLessThan(0L)
}
assertThat(node.name).isEqualTo(name)
if (fileName == "LayoutInspectorTreeTest.kt") {
assertThat(node.fileName).isEqualTo(fileName)
}
if (lineNumber != -1) {
assertThat(node.lineNumber).isEqualTo(lineNumber)
}
with(density) {
assertThat(node.left.toDp().value).isWithin(2.0f).of(left.value)
assertThat(node.top.toDp().value).isWithin(2.0f).of(top.value)
assertThat(node.width.toDp().value).isWithin(2.0f).of(width.value)
assertThat(node.height.toDp().value).isWithin(2.0f).of(height.value)
}
assertThat(node.children.map { it.name }).containsExactlyElementsIn(children).inOrder()
val receiver = ParameterValidationReceiver(node.parameters.listIterator())
receiver.block()
assertThat(receiver.parameterIterator.hasNext()).isFalse()
}
validate(
name = "Box",
fileName = "Box.kt",
left = 0.0.dp, top = 0.0.dp, width = viewWidth, height = viewHeight,
children = listOf("Column")
) {
parameter(name = "shape", type = ParameterType.String, value = "Shape")
parameter(name = "backgroundColor", type = ParameterType.Color, value = 0x0)
parameter(name = "padding", type = ParameterType.DimensionDp, value = 0.0f)
parameter(name = "paddingStart", type = ParameterType.DimensionDp, value = Float.NaN)
parameter(name = "paddingTop", type = ParameterType.DimensionDp, value = Float.NaN)
parameter(name = "paddingEnd", type = ParameterType.DimensionDp, value = Float.NaN)
parameter(name = "paddingBottom", type = ParameterType.DimensionDp, value = Float.NaN)
parameter(name = "gravity", type = ParameterType.String, value = "TopStart")
}
validate(
name = "Column",
fileName = "Box.kt",
left = 0.0.dp, top = 0.0.dp, width = viewWidth, height = viewHeight,
children = listOf("Column")
)
validate(
name = "Column",
fileName = "LayoutInspectorTreeTest.kt",
left = 0.0.dp,
top = 0.0.dp, width = 70.5.dp, height = 54.9.dp, children = listOf("Text", "Surface")
) {
parameter(name = "verticalArrangement", type = ParameterType.String, value = "Top")
parameter(name = "horizontalGravity", type = ParameterType.String, value = "Start")
}
validate(
name = "Text",
fileName = "LayoutInspectorTreeTest.kt",
left = 0.0.dp,
top = 0.0.dp, width = 70.5.dp, height = 18.9.dp, children = listOf("Text")
) {
parameter(name = "text", type = ParameterType.String, value = "Hello World")
parameter(name = "color", type = ParameterType.Color, value = 0xff00ff00.toInt())
parameter(name = "fontSize", type = ParameterType.String, value = "Inherit")
parameter(name = "letterSpacing", type = ParameterType.String, value = "Inherit")
parameter(name = "lineHeight", type = ParameterType.String, value = "Inherit")
parameter(name = "overflow", type = ParameterType.String, value = "Clip")
parameter(name = "softWrap", type = ParameterType.Boolean, value = true)
parameter(name = "maxLines", type = ParameterType.Int32, value = 2147483647)
}
validate(
name = "Text",
fileName = "Text.kt",
left = 0.0.dp,
top = 0.0.dp, width = 70.5.dp, height = 18.9.dp, children = listOf("CoreText")
) {
parameter(name = "text", type = ParameterType.String, value = "Hello World")
parameter(name = "color", type = ParameterType.Color, value = 0xff00ff00.toInt())
parameter(name = "fontSize", type = ParameterType.String, value = "Inherit")
parameter(name = "letterSpacing", type = ParameterType.String, value = "Inherit")
parameter(name = "lineHeight", type = ParameterType.String, value = "Inherit")
parameter(name = "overflow", type = ParameterType.String, value = "Clip")
parameter(name = "softWrap", type = ParameterType.Boolean, value = true)
parameter(name = "maxLines", type = ParameterType.Int32, value = 2147483647)
}
validate(
name = "CoreText",
fileName = "CoreText.kt",
isRenderNode = true,
left = 0.0.dp, top = 0.0.dp, width = 70.5.dp, height = 18.9.dp
) {
parameter(name = "text", type = ParameterType.String, value = "Hello World")
parameter(name = "style", type = ParameterType.String, value = "TextStyle") {
parameter(name = "color", type = ParameterType.Color, value = 0xff00ff00.toInt())
parameter(name = "fontSize", type = ParameterType.String, value = "Inherit")
parameter(name = "letterSpacing", type = ParameterType.String, value = "Inherit")
parameter(name = "background", type = ParameterType.String, value = "Unset")
parameter(name = "lineHeight", type = ParameterType.String, value = "Inherit")
}
parameter(name = "softWrap", type = ParameterType.Boolean, value = true)
parameter(name = "overflow", type = ParameterType.String, value = "Clip")
parameter(name = "maxLines", type = ParameterType.Int32, value = 2147483647)
}
validate(
name = "Surface",
fileName = "LayoutInspectorTreeTest.kt",
isRenderNode = true,
left = 0.0.dp,
top = 18.9.dp, width = 64.0.dp, height = 36.0.dp, children = listOf("Button")
) {
parameter(name = "shape", type = ParameterType.String, value = "Shape")
parameter(name = "color", type = ParameterType.Color, value = 0xffffffff.toInt())
parameter(name = "contentColor", type = ParameterType.Color, value = 0xff000000.toInt())
parameter(name = "elevation", type = ParameterType.DimensionDp, value = 0.0f)
}
validate(
name = "Button",
fileName = "LayoutInspectorTreeTest.kt",
left = 0.0.dp,
top = 18.9.dp, width = 64.0.dp, height = 36.0.dp, children = listOf("Surface")
) {
parameter(name = "enabled", type = ParameterType.Boolean, value = true)
parameter(name = "elevation", type = ParameterType.DimensionDp, value = 2.0f)
parameter(name = "disabledElevation", type = ParameterType.DimensionDp, value = 0.0f)
parameter(name = "shape", type = ParameterType.String, value = "RoundedCornerShape") {
parameter(name = "topLeft", type = ParameterType.DimensionDp, value = 4.0f)
parameter(name = "topRight", type = ParameterType.DimensionDp, value = 4.0f)
parameter(name = "bottomLeft", type = ParameterType.DimensionDp, value = 4.0f)
parameter(name = "bottomRight", type = ParameterType.DimensionDp, value = 4.0f)
}
parameter(
name = "backgroundColor", type = ParameterType.Color,
value = 0xff6200ee.toInt()
)
parameter(
name = "disabledBackgroundColor", type = ParameterType.Color,
value = 0xffe0e0e0.toInt()
)
parameter(name = "contentColor", type = ParameterType.Color, value = 0xffffffff.toInt())
parameter(name = "disabledContentColor", type = ParameterType.Color, value = 0x61000000)
parameter(name = "padding", type = ParameterType.String, value = "InnerPadding") {
parameter(name = "start", type = ParameterType.DimensionDp, value = 16.0f)
parameter(name = "end", type = ParameterType.DimensionDp, value = 16.0f)
parameter(name = "top", type = ParameterType.DimensionDp, value = 8.0f)
parameter(name = "bottom", type = ParameterType.DimensionDp, value = 8.0f)
}
}
validate(
name = "Surface",
fileName = "Button.kt",
isRenderNode = true,
left = 0.0.dp,
top = 18.9.dp, width = 64.0.dp, height = 36.0.dp, children = listOf("ProvideTextStyle")
) {
parameter(name = "shape", type = ParameterType.String, value = "RoundedCornerShape") {
parameter(name = "topLeft", type = ParameterType.DimensionDp, value = 4.0f)
parameter(name = "topRight", type = ParameterType.DimensionDp, value = 4.0f)
parameter(name = "bottomLeft", type = ParameterType.DimensionDp, value = 4.0f)
parameter(name = "bottomRight", type = ParameterType.DimensionDp, value = 4.0f)
}
parameter(name = "color", type = ParameterType.Color, value = 0xff6200ee.toInt())
parameter(name = "contentColor", type = ParameterType.Color, value = 0xffffffff.toInt())
parameter(name = "elevation", type = ParameterType.DimensionDp, value = 2.0f)
}
validate(
name = "ProvideTextStyle",
fileName = "Button.kt",
left = 16.0.dp,
top = 26.9.dp, width = 32.0.dp, height = 20.0.dp, children = listOf("Row")
) {
parameter(name = "value", type = ParameterType.String, value = "TextStyle") {
parameter(name = "color", type = ParameterType.String, value = "Unset")
parameter(name = "fontSize", type = ParameterType.DimensionSp, value = 14.0f)
parameter(name = "fontWeight", type = ParameterType.String, value = "Medium")
parameter(name = "fontFamily", type = ParameterType.String, value = "Default")
parameter(name = "letterSpacing", type = ParameterType.DimensionSp, value = 1.25f)
parameter(name = "background", type = ParameterType.String, value = "Unset")
parameter(name = "lineHeight", type = ParameterType.String, value = "Inherit")
}
}
validate(
name = "Row",
fileName = "Button.kt",
left = 16.0.dp,
top = 26.9.dp, width = 32.0.dp, height = 20.0.dp, children = listOf("Text")
) {
parameter(name = "horizontalArrangement", type = ParameterType.String, value = "Center")
parameter(
name = "verticalGravity", type = ParameterType.String,
value = "CenterVertically"
)
}
validate(
name = "Text",
fileName = "LayoutInspectorTreeTest.kt",
left = 21.8.dp,
top = 27.6.dp, width = 20.4.dp, height = 18.9.dp, children = listOf("Text")
) {
parameter(name = "text", type = ParameterType.String, value = "OK")
parameter(name = "color", type = ParameterType.String, value = "Unset")
parameter(name = "fontSize", type = ParameterType.String, value = "Inherit")
parameter(name = "letterSpacing", type = ParameterType.String, value = "Inherit")
parameter(name = "lineHeight", type = ParameterType.String, value = "Inherit")
parameter(name = "overflow", type = ParameterType.String, value = "Clip")
parameter(name = "softWrap", type = ParameterType.Boolean, value = true)
parameter(name = "maxLines", type = ParameterType.Int32, value = 2147483647)
}
validate(
name = "Text",
fileName = "Text.kt",
left = 21.8.dp,
top = 27.6.dp, width = 20.4.dp, height = 18.9.dp, children = listOf("CoreText")
) {
parameter(name = "text", type = ParameterType.String, value = "OK")
parameter(name = "color", type = ParameterType.String, value = "Unset")
parameter(name = "fontSize", type = ParameterType.String, value = "Inherit")
parameter(name = "letterSpacing", type = ParameterType.String, value = "Inherit")
parameter(name = "lineHeight", type = ParameterType.String, value = "Inherit")
parameter(name = "overflow", type = ParameterType.String, value = "Clip")
parameter(name = "softWrap", type = ParameterType.Boolean, value = true)
parameter(name = "maxLines", type = ParameterType.Int32, value = 2147483647)
}
validate(
name = "CoreText",
fileName = "CoreText.kt",
isRenderNode = true,
left = 21.8.dp, top = 27.6.dp, width = 20.4.dp, height = 18.9.dp
) {
parameter(name = "text", type = ParameterType.String, value = "OK")
parameter(name = "style", type = ParameterType.String, value = "TextStyle") {
parameter(name = "color", type = ParameterType.Color, value = 0xffffffff.toInt())
parameter(name = "fontSize", type = ParameterType.DimensionSp, value = 14.0f)
parameter(name = "fontWeight", type = ParameterType.String, value = "Medium")
parameter(name = "fontFamily", type = ParameterType.String, value = "Default")
parameter(name = "letterSpacing", type = ParameterType.DimensionSp, value = 1.25f)
parameter(name = "background", type = ParameterType.String, value = "Unset")
parameter(name = "lineHeight", type = ParameterType.String, value = "Inherit")
}
parameter(name = "softWrap", type = ParameterType.Boolean, value = true)
parameter(name = "overflow", type = ParameterType.String, value = "Clip")
parameter(name = "maxLines", type = ParameterType.Int32, value = 2147483647)
}
assertThat(nodeIterator.hasNext()).isFalse()
}
private fun readMySelf(): String {
return "hello"
}
private fun flatten(node: InspectorNode): List<InspectorNode> =
listOf(node).plus(node.children.flatMap { flatten(it) })
// region DEBUG print methods
private fun dumpNodes(nodes: List<InspectorNode>) {
@Suppress("ConstantConditionIf")
if (!DEBUG) {
return
}
println()
println("=================== Nodes ==========================")
nodes.forEach { dumpNode(it, indent = 0) }
println()
println("=================== validate statements ==========================")
nodes.forEach { generateValidate(it) }
}
private fun dumpNode(node: InspectorNode, indent: Int) {
println(
"\"${" ".repeat(indent * 2)}\", \"${node.name}\", \"${node.fileName}\", " +
"${node.lineNumber}, ${node.left}, ${node.top}, " +
"${node.width}, ${node.height}"
)
node.children.forEach { dumpNode(it, indent + 1) }
}
private fun generateValidate(node: InspectorNode) {
with(density) {
val left = round(node.left.toDp())
val top = round(node.top.toDp())
val width = if (node.width == view.width) "viewWidth" else round(node.width.toDp())
val height = if (node.height == view.height) "viewHeight" else round(node.height.toDp())
print(
"""
validate(
name = "${node.name}",
fileName = "${node.fileName}",
left = $left, top = $top, width = $width, height = $height""".trimIndent()
)
}
if (node.id > 0L) {
println(",")
print(" isRenderNode = true")
}
if (node.children.isNotEmpty()) {
println(",")
val children = node.children.joinToString { "\"${it.name}\"" }
print(" children = listOf($children)")
}
println()
print(")")
if (node.parameters.isNotEmpty()) {
generateParameters(node.parameters, 0)
}
println()
node.children.forEach { generateValidate(it) }
}
private fun generateParameters(parameters: List<NodeParameter>, indent: Int) {
val indentation = " ".repeat(indent * 2)
println(" {")
for (param in parameters) {
val name = param.name
val type = param.type
val value = toDisplayValue(type, param.value)
print("$indentation parameter(name = \"$name\", type = $type, value = $value)")
if (param.elements.isNotEmpty()) {
generateParameters(param.elements, indent + 1)
}
println()
}
print("$indentation}")
}
private fun toDisplayValue(type: ParameterType, value: Any?): String =
when (type) {
ParameterType.Boolean -> value.toString()
ParameterType.Color ->
"0x${Integer.toHexString(value as Int)}${if (value < 0) ".toInt()" else ""}"
ParameterType.DimensionSp,
ParameterType.DimensionDp -> "${value}f"
ParameterType.Int32 -> value.toString()
ParameterType.String -> "\"$value\""
else -> value?.toString() ?: "null"
}
private fun dumpSlotTableSet(slotTableRecord: SlotTableRecord) {
@Suppress("ConstantConditionIf")
if (!DEBUG) {
return
}
println()
println("=================== Groups ==========================")
slotTableRecord.store.forEach { dumpGroup(it.asTree(), indent = 0) }
}
private fun dumpGroup(group: Group, indent: Int) {
val position = group.position?.let { "\"$it\"" } ?: "null"
val box = group.box
val id = group.modifierInfo.mapNotNull { (it.extra as? OwnedLayer)?.layerId }
.singleOrNull() ?: 0
println(
"\"${" ".repeat(indent)}\", ${group.javaClass.simpleName}, \"${group.name}\", " +
"params: ${group.parameters.size}, children: ${group.children.size}, " +
"$id, $position, " +
"${box.left}, ${box.right}, ${box.right - box.left}, ${box.bottom - box.top}"
)
for (parameter in group.parameters) {
println("\"${" ".repeat(indent + 4)}\"- ${parameter.name}")
}
group.children.forEach { dumpGroup(it, indent + 1) }
}
private fun round(dp: Dp): Dp = Dp((dp.value * 10.0f).roundToInt() / 10.0f)
//endregion
}