Repository: cashapp/redwood
Pull request #387 (2022-09-26 19:41:31)

Delete old Gradle files from nested build
It no longer exists!


Patch:
diff --git a/redwood-flexbox-engine/src/commonMain/kotlin/app/cash/redwood/FlexboxEngine.kt b/redwood-flexbox-engine/src/commonMain/kotlin/app/cash/redwood/FlexboxEngine.kt
index 4910dd609..950edc715 100644
--- a/redwood-flexbox-engine/src/commonMain/kotlin/app/cash/redwood/FlexboxEngine.kt
+++ b/redwood-flexbox-engine/src/commonMain/kotlin/app/cash/redwood/FlexboxEngine.kt
@@ -264,7 +264,7 @@ public class FlexboxEngine {
           mode = mainMode,
           maxSize = mainSize,
           currentLength = flexLine.mainSize,
-          childLength = direction.mainMeasuredSize(child) + direction.mainMarginStart(child) + direction.mainMarginEnd(child),
+          childLength = direction.mainMeasuredSize(child) + direction.mainMarginStart(child) + direction.mainMarginEnd(child, ),
           flexItem = child,
           flexLinesSize = flexLines.size,
         )
diff --git a/redwood-flexbox-engine/src/commonMain/kotlin/app/cash/redwood/Node.kt b/redwood-flexbox-engine/src/commonMain/kotlin/app/cash/redwood/Node.kt
index 09b99cb32..2716e84ab 100644
--- a/redwood-flexbox-engine/src/commonMain/kotlin/app/cash/redwood/Node.kt
+++ b/redwood-flexbox-engine/src/commonMain/kotlin/app/cash/redwood/Node.kt
@@ -16,86 +16,89 @@
 package app.cash.redwood
 
 /**
- * A node with properties that can be measured and laid out inside a flexbox.
+ * An interface that has the common behavior as a node contained in a flexbox.
  */
-public class Node(
+public interface Node {
 
   /**
-   * The requested width of the node.
+   * The width attribute of the node.
    *
    * The attribute is about how wide the view wants to be. Can be one of the
    * constants [MatchParent] or [WrapContent], or an exact size.
    */
-  public val width: Int = WrapContent,
+  public val width: Int
 
   /**
-   * The requested height of the node.
+   * The height attribute of the node.
    *
    * The attribute is about how wide the view wants to be. Can be one of the
    * constants [MatchParent] or [WrapContent], or an exact size.
    */
-  public val height: Int = WrapContent,
+  public val height: Int
 
   /**
    * The minimum width attribute of the node.
    *
    * The attribute determines the minimum width the child can shrink to.
    */
-  public val minWidth: Int = 0,
+  public val minWidth: Int
 
   /**
    * The minimum height attribute of the node.
    *
    * The attribute determines the minimum height the child can shrink to.
    */
-  public val minHeight: Int = 0,
+  public val minHeight: Int
 
   /**
    * The maximum width attribute of the node.
    *
    * The attribute determines the maximum width the child can expand to.
    */
-  public val maxWidth: Int = Int.MAX_VALUE,
+  public val maxWidth: Int
 
   /**
    * The maximum height attribute of the node.
    */
-  public val maxHeight: Int = Int.MAX_VALUE,
+  public val maxHeight: Int
 
   /**
    * True if this item is visible and should be laid out.
    */
-  public val visible: Boolean = true,
+  public val visible: Boolean
 
   /**
    * The baseline used for [AlignItems.Baseline] and [AlignSelf.Baseline].
+   * If not specified, -1 is set as a default value.
    */
-  public val baseline: Int = -1,
+  public val baseline: Int
 
   /**
    * The order attribute of the node.
    *
    * The attribute can change the order in which the children are laid out.
    * By default, children are displayed and laid out in the same order as they added to the
-   * [FlexboxEngine].
+   * [FlexboxEngine]. If not specified, [DefaultOrder] is set as a default value.
    */
-  public val order: Int = DefaultOrder,
+  public val order: Int
 
   /**
    * The flex grow attribute of the node.
    *
    * The attribute determines how much this child will grow if positive free space is
    * distributed relative to the rest of other nodes included in the same flex line.
+   * If not specified, [DefaultFlexGrow] is set as a default value.
    */
-  public val flexGrow: Float = DefaultFlexGrow,
+  public val flexGrow: Float
 
   /**
    * The flex shrink attribute of the node.
    *
    * The attribute determines how much this child will shrink if negative free space is
    * distributed relative to the rest of other nodes included in the same flex line.
+   * If not specified, [DefaultFlexShrink] is set as a default value.
    */
-  public val flexShrink: Float = DefaultFlexShrink,
+  public val flexShrink: Float
 
   /**
    * The flexBasisPercent attribute of the node.
@@ -109,7 +112,7 @@ public class Node(
    * This attribute is only effective when the parent's MeasureSpec mode is
    * MeasureSpec.EXACTLY. The default value is -1, which means not set.
    */
-  public val flexBasisPercent: Float = DefaultFlexBasisPercent,
+  public val flexBasisPercent: Float
 
   /**
    * The align self attribute of the node.
@@ -121,8 +124,9 @@ public class Node(
    * The value needs to be one of the values in ([AlignSelf.Auto],
    * [AlignItems.Stretch], [AlignItems.FlexStart], [AlignItems.FlexEnd],
    * [AlignItems.Center], or [AlignItems.Baseline]).
+   * If not specified, [AlignSelf.Auto] is set as a default value.
    */
-  public val alignSelf: AlignSelf = AlignSelf.Auto,
+  public val alignSelf: AlignSelf
 
   /**
    * The wrapBefore attribute of the node.
@@ -136,42 +140,33 @@ public class Node(
    * the layouts when building a grid like layout or for a situation where developers want
    * to put a new flex line to make a semantic difference from the previous one, etc.
    */
-  public val wrapBefore: Boolean = false,
+  public val wrapBefore: Boolean
 
   /**
    * The margin of the node.
    */
-  public val margin: Spacing = Spacing.Zero,
-) {
+  public val margin: Spacing
 
   /**
    * The measured width after invoking [measure].
-   *
-   * TODO: Remove this mutable attribute and use the returned [Size] from [measure].
    */
-  public var measuredWidth: Int = -1
+  public val measuredWidth: Int
 
   /**
    * The measured height after invoking [measure].
-   *
-   * TODO: Remove this mutable attribute and use the returned [Size] from [measure].
    */
-  public var measuredHeight: Int = -1
+  public val measuredHeight: Int
 
   /**
-   * A callback to to measure this node according to the `widthSpec` and `heightSpec` constraints
-   * and update [measuredWidth] and [measuredHeight] with the result.
+   * Measure this node according to the [widthSpec] and [heightSpec] constraints and update
+   * [measuredWidth] and [measuredHeight] with the result.
    */
-  public var measure: (widthSpec: MeasureSpec, heightSpec: MeasureSpec) -> Size = { widthSpec, heightSpec ->
-    measuredWidth = MeasureSpec.resolveSize(width, widthSpec)
-    measuredHeight = MeasureSpec.resolveSize(height, heightSpec)
-    Size(measuredWidth, measuredHeight)
-  }
+  public fun measure(widthSpec: MeasureSpec, heightSpec: MeasureSpec)
 
   /**
-   * A callback to place the node inside the given `left`, `top`, `right`, and `bottom` coordinates.
+   * Place the node inside the given bounding box.
    */
-  public var layout: (left: Int, top: Int, right: Int, bottom: Int) -> Unit = { _, _, _, _ -> }
+  public fun layout(left: Int, top: Int, right: Int, bottom: Int)
 
   public companion object {
     /**
diff --git a/redwood-flexbox-engine/src/commonMain/kotlin/app/cash/redwood/properties.kt b/redwood-flexbox-engine/src/commonMain/kotlin/app/cash/redwood/properties.kt
index 9010f2fe7..1b816d700 100644
--- a/redwood-flexbox-engine/src/commonMain/kotlin/app/cash/redwood/properties.kt
+++ b/redwood-flexbox-engine/src/commonMain/kotlin/app/cash/redwood/properties.kt
@@ -207,16 +207,6 @@ public value class MeasureSpec private constructor(internal val value: Int) {
       // Use the top 2 bits for the mode and use the bottom 30 bits for the size.
       return MeasureSpec((mode.ordinal shl 30) or (size and 0x3FFF))
     }
-
-    /** A convenience function to constrain [size] inside a given [measureSpec]. */
-    public fun resolveSize(size: Int, measureSpec: MeasureSpec): Int {
-      return when (measureSpec.mode) {
-        MeasureSpecMode.AtMost -> minOf(measureSpec.size, size)
-        MeasureSpecMode.Exactly -> measureSpec.size
-        MeasureSpecMode.Unspecified -> size
-        else -> throw AssertionError()
-      }
-    }
   }
 }
 
@@ -287,8 +277,4 @@ public data class Size(
       "invalid Size: [$width, $height]"
     }
   }
-
-  public companion object {
-    public val Zero: Size = Size(0, 0)
-  }
 }
diff --git a/redwood-flexbox-engine/src/commonMain/kotlin/app/cash/redwood/utils.kt b/redwood-flexbox-engine/src/commonMain/kotlin/app/cash/redwood/utils.kt
index f8e956018..5a34b3262 100644
--- a/redwood-flexbox-engine/src/commonMain/kotlin/app/cash/redwood/utils.kt
+++ b/redwood-flexbox-engine/src/commonMain/kotlin/app/cash/redwood/utils.kt
@@ -30,12 +30,6 @@ internal fun unpackHigher(value: Long): Int {
   return (value shr 32).toInt()
 }
 
-/** Convenience function to use named arguments. */
-@Suppress("EXTENSION_FUNCTION_SHADOWED_BY_MEMBER_PROPERTY_WITH_INVOKE")
-internal inline fun Node.layout(left: Int, top: Int, right: Int, bottom: Int) {
-  layout(left, top, right, bottom)
-}
-
 /**
  * The number of children who are not invisible in this flex line.
  */
@@ -94,3 +88,12 @@ internal fun MeasureSpec.Companion.getChildMeasureSpec(
   }
   return from(resultSize, resultMode)
 }
+
+internal fun MeasureSpec.Companion.resolveSize(size: Int, measureSpec: MeasureSpec): Int {
+  return when (measureSpec.mode) {
+    MeasureSpecMode.AtMost -> minOf(measureSpec.size, size)
+    MeasureSpecMode.Exactly -> measureSpec.size
+    MeasureSpecMode.Unspecified -> size
+    else -> throw AssertionError()
+  }
+}
diff --git a/samples/gradlew b/samples/gradlew
deleted file mode 100755
index 1b6c78733..000000000
--- a/samples/gradlew
+++ /dev/null
@@ -1,234 +0,0 @@
-#!/bin/sh
-
-#
-# Copyright © 2015-2021 the original authors.
-#
-# 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
-#
-#      https://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.
-#
-
-##############################################################################
-#
-#   Gradle start up script for POSIX generated by Gradle.
-#
-#   Important for running:
-#
-#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
-#       noncompliant, but you have some other compliant shell such as ksh or
-#       bash, then to run this script, type that shell name before the whole
-#       command line, like:
-#
-#           ksh Gradle
-#
-#       Busybox and similar reduced shells will NOT work, because this script
-#       requires all of these POSIX shell features:
-#         * functions;
-#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
-#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
-#         * compound commands having a testable exit status, especially «case»;
-#         * various built-in commands including «command», «set», and «ulimit».
-#
-#   Important for patching:
-#
-#   (2) This script targets any POSIX shell, so it avoids extensions provided
-#       by Bash, Ksh, etc; in particular arrays are avoided.
-#
-#       The "traditional" practice of packing multiple parameters into a
-#       space-separated string is a well documented source of bugs and security
-#       problems, so this is (mostly) avoided, by progressively accumulating
-#       options in "$@", and eventually passing that to Java.
-#
-#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
-#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
-#       see the in-line comments for details.
-#
-#       There are tweaks for specific operating systems such as AIX, CygWin,
-#       Darwin, MinGW, and NonStop.
-#
-#   (3) This script is generated from the Groovy template
-#       https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
-#       within the Gradle project.
-#
-#       You can find Gradle at https://github.com/gradle/gradle/.
-#
-##############################################################################
-
-# Attempt to set APP_HOME
-
-# Resolve links: $0 may be a link
-app_path=$0
-
-# Need this for daisy-chained symlinks.
-while
-    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
-    [ -h "$app_path" ]
-do
-    ls=$( ls -ld "$app_path" )
-    link=${ls#*' -> '}
-    case $link in             #(
-      /*)   app_path=$link ;; #(
-      *)    app_path=$APP_HOME$link ;;
-    esac
-done
-
-APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
-
-APP_NAME="Gradle"
-APP_BASE_NAME=${0##*/}
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD=maximum
-
-warn () {
-    echo "$*"
-} >&2
-
-die () {
-    echo
-    echo "$*"
-    echo
-    exit 1
-} >&2
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-nonstop=false
-case "$( uname )" in                #(
-  CYGWIN* )         cygwin=true  ;; #(
-  Darwin* )         darwin=true  ;; #(
-  MSYS* | MINGW* )  msys=true    ;; #(
-  NONSTOP* )        nonstop=true ;;
-esac
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
-    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
-        # IBM's JDK on AIX uses strange locations for the executables
-        JAVACMD=$JAVA_HOME/jre/sh/java
-    else
-        JAVACMD=$JAVA_HOME/bin/java
-    fi
-    if [ ! -x "$JAVACMD" ] ; then
-        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-    fi
-else
-    JAVACMD=java
-    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-fi
-
-# Increase the maximum file descriptors if we can.
-if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
-    case $MAX_FD in #(
-      max*)
-        MAX_FD=$( ulimit -H -n ) ||
-            warn "Could not query maximum file descriptor limit"
-    esac
-    case $MAX_FD in  #(
-      '' | soft) :;; #(
-      *)
-        ulimit -n "$MAX_FD" ||
-            warn "Could not set maximum file descriptor limit to $MAX_FD"
-    esac
-fi
-
-# Collect all arguments for the java command, stacking in reverse order:
-#   * args from the command line
-#   * the main class name
-#   * -classpath
-#   * -D...appname settings
-#   * --module-path (only if needed)
-#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
-
-# For Cygwin or MSYS, switch paths to Windows format before running java
-if "$cygwin" || "$msys" ; then
-    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
-    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
-
-    JAVACMD=$( cygpath --unix "$JAVACMD" )
-
-    # Now convert the arguments - kludge to limit ourselves to /bin/sh
-    for arg do
-        if
-            case $arg in                                #(
-              -*)   false ;;                            # don't mess with options #(
-              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
-                    [ -e "$t" ] ;;                      #(
-              *)    false ;;
-            esac
-        then
-            arg=$( cygpath --path --ignore --mixed "$arg" )
-        fi
-        # Roll the args list around exactly as many times as the number of
-        # args, so each arg winds up back in the position where it started, but
-        # possibly modified.
-        #
-        # NB: a `for` loop captures its iteration list before it begins, so
-        # changing the positional parameters here affects neither the number of
-        # iterations, nor the values presented in `arg`.
-        shift                   # remove old arg
-        set -- "$@" "$arg"      # push replacement arg
-    done
-fi
-
-# Collect all arguments for the java command;
-#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
-#     shell script including quotes and variable substitutions, so put them in
-#     double quotes to make sure that they get re-expanded; and
-#   * put everything else in single quotes, so that it's not re-expanded.
-
-set -- \
-        "-Dorg.gradle.appname=$APP_BASE_NAME" \
-        -classpath "$CLASSPATH" \
-        org.gradle.wrapper.GradleWrapperMain \
-        "$@"
-
-# Use "xargs" to parse quoted args.
-#
-# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
-#
-# In Bash we could simply go:
-#
-#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
-#   set -- "${ARGS[@]}" "$@"
-#
-# but POSIX shell has neither arrays nor command substitution, so instead we
-# post-process each arg (as a line of input to sed) to backslash-escape any
-# character that might be a shell metacharacter, then use eval to reverse
-# that process (while maintaining the separation between arguments), and wrap
-# the whole thing up as a single "set" statement.
-#
-# This will of course break if any of these variables contains a newline or
-# an unmatched quote.
-#
-
-eval "set -- $(
-        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
-        xargs -n1 |
-        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
-        tr '\n' ' '
-    )" '"$@"'
-
-exec "$JAVACMD" "$@"
diff --git a/samples/gradlew.bat b/samples/gradlew.bat
deleted file mode 100644
index ac1b06f93..000000000
--- a/samples/gradlew.bat
+++ /dev/null
@@ -1,89 +0,0 @@
-@rem
-@rem Copyright 2015 the original author or authors.
-@rem
-@rem Licensed under the Apache License, Version 2.0 (the "License");
-@rem you may not use this file except in compliance with the License.
-@rem You may obtain a copy of the License at
-@rem
-@rem      https://www.apache.org/licenses/LICENSE-2.0
-@rem
-@rem Unless required by applicable law or agreed to in writing, software
-@rem distributed under the License is distributed on an "AS IS" BASIS,
-@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-@rem See the License for the specific language governing permissions and
-@rem limitations under the License.
-@rem
-
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem  Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Resolve any "." and ".." in APP_HOME to make it shorter.
-for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto execute
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto execute
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega


Test patch:
diff --git a/redwood-flexbox-engine/src/commonTest/kotlin/app/cash/redwood/FakeNode.kt b/redwood-flexbox-engine/src/commonTest/kotlin/app/cash/redwood/FakeNode.kt
new file mode 100644
index 000000000..202c24052
--- /dev/null
+++ b/redwood-flexbox-engine/src/commonTest/kotlin/app/cash/redwood/FakeNode.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 Square, Inc.
+ *
+ * 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 app.cash.redwood
+
+import app.cash.redwood.Node.Companion.DefaultFlexBasisPercent
+import app.cash.redwood.Node.Companion.DefaultFlexGrow
+import app.cash.redwood.Node.Companion.DefaultFlexShrink
+import app.cash.redwood.Node.Companion.DefaultOrder
+import app.cash.redwood.Node.Companion.WrapContent
+
+class FakeNode(
+  override val width: Int = WrapContent,
+  override val height: Int = WrapContent,
+  override val minWidth: Int = 0,
+  override val minHeight: Int = 0,
+  override val maxWidth: Int = Int.MAX_VALUE,
+  override val maxHeight: Int = Int.MAX_VALUE,
+  override val visible: Boolean = true,
+  override val baseline: Int = -1,
+  override val order: Int = DefaultOrder,
+  override val flexGrow: Float = DefaultFlexGrow,
+  override val flexShrink: Float = DefaultFlexShrink,
+  override val flexBasisPercent: Float = DefaultFlexBasisPercent,
+  override val alignSelf: AlignSelf = AlignSelf.Auto,
+  override val wrapBefore: Boolean = false,
+  override val margin: Spacing = Spacing.Zero,
+) : Node {
+
+  override var measuredWidth: Int = -1
+  override var measuredHeight: Int = -1
+
+  override fun measure(widthSpec: MeasureSpec, heightSpec: MeasureSpec) {
+    measuredWidth = MeasureSpec.resolveSize(width, widthSpec)
+    measuredHeight = MeasureSpec.resolveSize(height, heightSpec)
+  }
+
+  override fun layout(left: Int, top: Int, right: Int, bottom: Int) {}
+}
diff --git a/redwood-flexbox-engine/src/commonTest/kotlin/app/cash/redwood/FlexboxEngineTest.kt b/redwood-flexbox-engine/src/commonTest/kotlin/app/cash/redwood/FlexboxEngineTest.kt
index f2a4811d6..1868fe7cf 100644
--- a/redwood-flexbox-engine/src/commonTest/kotlin/app/cash/redwood/FlexboxEngineTest.kt
+++ b/redwood-flexbox-engine/src/commonTest/kotlin/app/cash/redwood/FlexboxEngineTest.kt
@@ -31,10 +31,10 @@ class FlexboxEngineTest {
 
   @Test
   fun testCalculateHorizontalFlexLines() {
-    val node1 = Node(100, 100)
-    val node2 = Node(200, 100)
-    val node3 = Node(300, 100)
-    val node4 = Node(400, 100)
+    val node1 = FakeNode(100, 100)
+    val node2 = FakeNode(200, 100)
+    val node3 = FakeNode(300, 100)
+    val node4 = FakeNode(400, 100)
     engine.nodes += node1
     engine.nodes += node2
     engine.nodes += node3
@@ -73,10 +73,10 @@ class FlexboxEngineTest {
 
   @Test
   fun testCalculateVerticalFlexLines() {
-    val node1 = Node(100, 100)
-    val node2 = Node(100, 200)
-    val node3 = Node(100, 300)
-    val node4 = Node(100, 400)
+    val node1 = FakeNode(100, 100)
+    val node2 = FakeNode(100, 200)
+    val node3 = FakeNode(100, 300)
+    val node4 = FakeNode(100, 400)
     engine.nodes += node1
     engine.nodes += node2
     engine.nodes += node3
@@ -116,10 +116,10 @@ class FlexboxEngineTest {
 
   @Test
   fun testDetermineMainSize_direction_row_flexGrowSet() {
-    val node1 = Node(100, 100)
-    val node2 = Node(200, 100, flexGrow = 1.0f)
-    val node3 = Node(300, 100)
-    val node4 = Node(400, 100, flexGrow = 2.0f)
+    val node1 = FakeNode(100, 100)
+    val node2 = FakeNode(200, 100, flexGrow = 1.0f)
+    val node3 = FakeNode(300, 100)
+    val node4 = FakeNode(400, 100, flexGrow = 2.0f)
     engine.nodes += node1
     engine.nodes += node2
     engine.nodes += node3
@@ -145,10 +145,10 @@ class FlexboxEngineTest {
 
   @Test
   fun testDetermineMainSize_direction_column_flexGrowSet() {
-    val node1 = Node(100, 100)
-    val node2 = Node(100, 200, flexGrow = 1.0f)
-    val node3 = Node(100, 300)
-    val node4 = Node(100, 400, flexGrow = 2.0f)
+    val node1 = FakeNode(100, 100)
+    val node2 = FakeNode(100, 200, flexGrow = 1.0f)
+    val node3 = FakeNode(100, 300)
+    val node4 = FakeNode(100, 400, flexGrow = 2.0f)
     engine.nodes += node1
     engine.nodes += node2
     engine.nodes += node3
@@ -174,10 +174,10 @@ class FlexboxEngineTest {
 
   @Test
   fun testDetermineMainSize_direction_row_flexShrinkSet() {
-    val node1 = Node(200, 100)
-    val node2 = Node(200, 100)
-    val node3 = Node(200, 100)
-    val node4 = Node(200, 100)
+    val node1 = FakeNode(200, 100)
+    val node2 = FakeNode(200, 100)
+    val node3 = FakeNode(200, 100)
+    val node4 = FakeNode(200, 100)
     engine.nodes += node1
     engine.nodes += node2
     engine.nodes += node3
@@ -203,10 +203,10 @@ class FlexboxEngineTest {
 
   @Test
   fun testDetermineMainSize_direction_column_flexShrinkSet() {
-    val node1 = Node(100, 200)
-    val node2 = Node(100, 200)
-    val node3 = Node(100, 200)
-    val node4 = Node(100, 200)
+    val node1 = FakeNode(100, 200)
+    val node2 = FakeNode(100, 200)
+    val node3 = FakeNode(100, 200)
+    val node4 = FakeNode(100, 200)
     engine.nodes += node1
     engine.nodes += node2
     engine.nodes += node3
@@ -232,8 +232,8 @@ class FlexboxEngineTest {
 
   @Test
   fun testDetermineMainSize_directionRow_fixedSizeViewAndShrinkable_doNotExceedMaxMainSize() {
-    val node1 = Node(100, 100, flexShrink = 0f)
-    val node2 = Node(2000, 2000) // simulate a very long text view
+    val node1 = FakeNode(100, 100, flexShrink = 0f)
+    val node2 = FakeNode(2000, 2000) // simulate a very long text view
     engine.nodes += node1
     engine.nodes += node2
     engine.flexWrap = FlexWrap.NoWrap
@@ -250,9 +250,9 @@ class FlexboxEngineTest {
 
   @Test
   fun testDetermineMainSize_directionRow_twoFixedSizeViewsAndShrinkable_doNotExceedMaxMainSize() {
-    val node1 = Node(100, 100, flexShrink = 0f)
-    val node2 = Node(2000, 2000) // simulate a very long text view
-    val node3 = Node(100, 100, flexShrink = 0f)
+    val node1 = FakeNode(100, 100, flexShrink = 0f)
+    val node2 = FakeNode(2000, 2000) // simulate a very long text view
+    val node3 = FakeNode(100, 100, flexShrink = 0f)
     engine.nodes += node1
     engine.nodes += node2
     engine.nodes += node3
@@ -271,10 +271,10 @@ class FlexboxEngineTest {
 
   @Test
   fun testDetermineCrossSize_direction_row_alignContent_stretch() {
-    val node1 = Node(100, 100)
-    val node2 = Node(200, 100)
-    val node3 = Node(300, 100)
-    val node4 = Node(400, 100)
+    val node1 = FakeNode(100, 100)
+    val node2 = FakeNode(200, 100)
+    val node3 = FakeNode(300, 100)
+    val node4 = FakeNode(400, 100)
     engine.nodes += node1
     engine.nodes += node2
     engine.nodes += node3
@@ -300,10 +300,10 @@ class FlexboxEngineTest {
 
   @Test
   fun testDetermineCrossSize_direction_column_alignContent_stretch() {
-    val node1 = Node(100, 100)
-    val node2 = Node(100, 200)
-    val node3 = Node(100, 300)
-    val node4 = Node(100, 400)
+    val node1 = FakeNode(100, 100)
+    val node2 = FakeNode(100, 200)
+    val node3 = FakeNode(100, 300)
+    val node4 = FakeNode(100, 400)
     engine.nodes += node1
     engine.nodes += node2
     engine.nodes += node3
@@ -365,10 +365,10 @@ class FlexboxEngineTest {
 
   @Test
   fun testFlexLine_anyItemsHaveFlexGrow() {
-    val node1 = Node(100, 100, flexGrow = 1.0f)
-    val node2 = Node(100, 200)
-    val node3 = Node(100, 300)
-    val node4 = Node(100, 400, flexGrow = 2.0f)
+    val node1 = FakeNode(100, 100, flexGrow = 1.0f)
+    val node2 = FakeNode(100, 200)
+    val node3 = FakeNode(100, 300)
+    val node4 = FakeNode(100, 400, flexGrow = 2.0f)
     engine.apply {
       nodes += node1
       nodes += node2