From 7f889fcf0893a9d6c76e9704742a8aadebc7739c Mon Sep 17 00:00:00 2001 From: Thiago Brezinski Date: Mon, 14 Aug 2023 20:11:26 +0100 Subject: [PATCH 1/3] Convert ReactPropConstantsTest to Kotlin --- .../uimanager/ViewManagersPropertyCache.java | 4 + .../uimanager/ReactPropConstantsTest.java | 165 ------------------ .../react/uimanager/ReactPropConstantsTest.kt | 153 ++++++++++++++++ 3 files changed, 157 insertions(+), 165 deletions(-) delete mode 100644 packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropConstantsTest.java create mode 100644 packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropConstantsTest.kt diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java index 438ec3fd0b57c6..f177d882973875 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java @@ -393,6 +393,10 @@ public BoxedColorPropSetter(ReactProp prop, Method setter) { */ /*package*/ static Map getNativePropSettersForShadowNodeClass( Class cls) { + if(cls == null) { + return new HashMap<>(); + }; + for (Class iface : cls.getInterfaces()) { if (iface == ReactShadowNode.class) { return EMPTY_PROPS_MAP; diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropConstantsTest.java b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropConstantsTest.java deleted file mode 100644 index 121af024f0270a..00000000000000 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropConstantsTest.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.uimanager; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; - -import android.view.View; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.common.MapBuilder; -import com.facebook.react.uimanager.annotations.ReactProp; -import com.facebook.react.uimanager.annotations.ReactPropGroup; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.modules.junit4.rule.PowerMockRule; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -/** Verifies that prop constants are generated properly based on {@code ReactProp} annotation. */ -@RunWith(RobolectricTestRunner.class) -@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "androidx.*", "android.*"}) -@Ignore // TODO T14964130 -public class ReactPropConstantsTest { - - @Rule public PowerMockRule rule = new PowerMockRule(); - - private class ViewManagerUnderTest extends ViewManager { - - @Override - public String getName() { - return "SomeView"; - } - - @Override - public ReactShadowNode createShadowNodeInstance() { - fail("This method should not be executed as a part of this test"); - return null; - } - - @Override - protected View createViewInstance(ThemedReactContext reactContext) { - fail("This method should not be executed as a part of this test"); - return null; - } - - @Override - public Class getShadowNodeClass() { - return ReactShadowNode.class; - } - - @Override - public void updateExtraData(View root, Object extraData) { - fail("This method should not be executed as a part of this test"); - } - - @ReactProp(name = "boolProp") - public void setBoolProp(View v, boolean value) {} - - @ReactProp(name = "intProp") - public void setIntProp(View v, int value) {} - - @ReactProp(name = "floatProp") - public void setFloatProp(View v, float value) {} - - @ReactProp(name = "doubleProp") - public void setDoubleProp(View v, double value) {} - - @ReactProp(name = "stringProp") - public void setStringProp(View v, String value) {} - - @ReactProp(name = "boxedBoolProp") - public void setBoxedBoolProp(View v, Boolean value) {} - - @ReactProp(name = "boxedIntProp") - public void setBoxedIntProp(View v, Integer value) {} - - @ReactProp(name = "arrayProp") - public void setArrayProp(View v, ReadableArray value) {} - - @ReactProp(name = "mapProp") - public void setMapProp(View v, ReadableMap value) {} - - @ReactPropGroup( - names = { - "floatGroupPropFirst", - "floatGroupPropSecond", - }) - public void setFloatGroupProp(View v, int index, float value) {} - - @ReactPropGroup(names = {"intGroupPropFirst", "intGroupPropSecond"}) - public void setIntGroupProp(View v, int index, int value) {} - - @ReactPropGroup( - names = { - "boxedIntGroupPropFirst", - "boxedIntGroupPropSecond", - }) - public void setBoxedIntGroupProp(View v, int index, Integer value) {} - - @ReactProp(name = "customIntProp", customType = "date") - public void customIntProp(View v, int value) {} - - @ReactPropGroup( - names = {"customBoxedIntGroupPropFirst", "customBoxedIntGroupPropSecond"}, - customType = "color") - public void customIntGroupProp(View v, int index, Integer value) {} - } - - @Test - public void testNativePropsIncludeCorrectTypes() { - List viewManagers = Arrays.asList(new ViewManagerUnderTest()); - ReactApplicationContext reactContext = - new ReactApplicationContext(RuntimeEnvironment.getApplication()); - UIManagerModule uiManagerModule = new UIManagerModule(reactContext, viewManagers, 0); - Map constants = - (Map) valueAtPath(uiManagerModule.getConstants(), "SomeView", "NativeProps"); - assertThat(constants) - .isEqualTo( - MapBuilder.builder() - .put("boolProp", "boolean") - .put("intProp", "number") - .put("doubleProp", "number") - .put("floatProp", "number") - .put("stringProp", "String") - .put("boxedBoolProp", "boolean") - .put("boxedIntProp", "number") - .put("arrayProp", "Array") - .put("mapProp", "Map") - .put("floatGroupPropFirst", "number") - .put("floatGroupPropSecond", "number") - .put("intGroupPropFirst", "number") - .put("intGroupPropSecond", "number") - .put("boxedIntGroupPropFirst", "number") - .put("boxedIntGroupPropSecond", "number") - .put("customIntProp", "date") - .put("customBoxedIntGroupPropFirst", "color") - .put("customBoxedIntGroupPropSecond", "color") - .build()); - } - - private static Object valueAtPath(Map nestedMap, String... keyPath) { - assertThat(keyPath).isNotEmpty(); - Object value = nestedMap; - for (String key : keyPath) { - assertThat(value).isInstanceOf(Map.class); - nestedMap = (Map) value; - assertThat(nestedMap).containsKey(key); - value = nestedMap.get(key); - } - return value; - } -} diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropConstantsTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropConstantsTest.kt new file mode 100644 index 00000000000000..86dae2ff69d290 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropConstantsTest.kt @@ -0,0 +1,153 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +package com.facebook.react.uimanager + +import android.view.View +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReadableArray +import com.facebook.react.bridge.ReadableMap +import com.facebook.react.common.MapBuilder +import com.facebook.react.uimanager.annotations.ReactProp +import com.facebook.react.uimanager.annotations.ReactPropGroup +import org.assertj.core.api.Assertions +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment + +/** Verifies that prop constants are generated properly based on `ReactProp` annotation. */ +@RunWith(RobolectricTestRunner::class) + +class ReactPropConstantsTest { + private inner class ViewManagerUnderTest : ViewManager?>() { + override fun getName(): String { + return "SomeView" + } + + override fun createShadowNodeInstance(): ReactShadowNode<*>? { + Assertions.fail("This method should not be executed as a part of this test") + return null + } + + override fun createViewInstance(reactContext: ThemedReactContext): View { + Assertions.fail("This method should not be executed as a part of this test") + return View(reactContext) + } + + override fun getShadowNodeClass(): Class> { + return ReactShadowNode::class.java + } + + override fun updateExtraData(root: View, extraData: Any) { + Assertions.fail("This method should not be executed as a part of this test") + } + + @ReactProp(name = "boolProp") + fun setBoolProp(v: View?, value: Boolean) { + } + + @ReactProp(name = "intProp") + fun setIntProp(v: View?, value: Int) { + } + + @ReactProp(name = "floatProp") + fun setFloatProp(v: View?, value: Float) { + } + + @ReactProp(name = "doubleProp") + fun setDoubleProp(v: View?, value: Double) { + } + + @ReactProp(name = "stringProp") + fun setStringProp(v: View?, value: String?) { + } + + @ReactProp(name = "boxedBoolProp") + fun setBoxedBoolProp(v: View?, value: Boolean?) { + } + + @ReactProp(name = "boxedIntProp") + fun setBoxedIntProp(v: View?, value: Int?) { + } + + @ReactProp(name = "arrayProp") + fun setArrayProp(v: View?, value: ReadableArray?) { + } + + @ReactProp(name = "mapProp") + fun setMapProp(v: View?, value: ReadableMap?) { + } + + @ReactPropGroup(names = ["floatGroupPropFirst", "floatGroupPropSecond"]) + fun setFloatGroupProp(v: View?, index: Int, value: Float) { + } + + @ReactPropGroup(names = ["intGroupPropFirst", "intGroupPropSecond"]) + fun setIntGroupProp(v: View?, index: Int, value: Int) { + } + + @ReactPropGroup(names = ["boxedIntGroupPropFirst", "boxedIntGroupPropSecond"]) + fun setBoxedIntGroupProp(v: View?, index: Int, value: Int?) { + } + + @ReactProp(name = "customIntProp", customType = "date") + fun customIntProp(v: View?, value: Int) { + } + + @ReactPropGroup( + names = ["customBoxedIntGroupPropFirst", "customBoxedIntGroupPropSecond"], + customType = "color" + ) + fun customIntGroupProp(v: View?, index: Int, value: Int?) { + } + } + + @Test + fun testNativePropsIncludeCorrectTypes() { + val viewManagers = listOf>(ViewManagerUnderTest()) + val reactContext = ReactApplicationContext(RuntimeEnvironment.getApplication()) + val uiManagerModule = UIManagerModule(reactContext, viewManagers, 0) + val constants: Map<*, *>? = valueAtPath(uiManagerModule.constants, "SomeView", "NativeProps") + + Assertions.assertThat(constants) + .isEqualTo( + MapBuilder.builder() + .put("boolProp", "boolean") + .put("intProp", "number") + .put("doubleProp", "number") + .put("floatProp", "number") + .put("stringProp", "String") + .put("boxedBoolProp", "boolean") + .put("boxedIntProp", "number") + .put("arrayProp", "Array") + .put("mapProp", "Map") + .put("floatGroupPropFirst", "number") + .put("floatGroupPropSecond", "number") + .put("intGroupPropFirst", "number") + .put("intGroupPropSecond", "number") + .put("boxedIntGroupPropFirst", "number") + .put("boxedIntGroupPropSecond", "number") + .put("customIntProp", "date") + .put("customBoxedIntGroupPropFirst", "color") + .put("customBoxedIntGroupPropSecond", "color") + .build() + ) + } + + companion object { + private fun valueAtPath(nestedMap: Map?, vararg keyPath: String): Map<*, *>? { + Assertions.assertThat(keyPath).isNotEmpty + var value = nestedMap + for (key in keyPath) { + Assertions.assertThat(value).isInstanceOf(Map::class.java) + Assertions.assertThat(value).containsKey(key) + value = (value?.get(key) as? Map ?: error("Value not found for key: $key")) + } + return value + } + } +} From f5f07d8dbcdc4d2ffb4672016993ad78d7d2496e Mon Sep 17 00:00:00 2001 From: Thiago Brezinski Date: Wed, 16 Aug 2023 13:00:18 +0100 Subject: [PATCH 2/3] Suppress unused params, fix valueAtPath typings --- .../react/uimanager/ReactPropConstantsTest.kt | 118 +++++++----------- 1 file changed, 47 insertions(+), 71 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropConstantsTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropConstantsTest.kt index 86dae2ff69d290..aaa11bb50f3534 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropConstantsTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropConstantsTest.kt @@ -19,10 +19,10 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.RuntimeEnvironment -/** Verifies that prop constants are generated properly based on `ReactProp` annotation. */ +/** Verifies that prop constants are generated properly based on `ReactProp` annotation. */ @RunWith(RobolectricTestRunner::class) - class ReactPropConstantsTest { + @Suppress("UNUSED_PARAMETER") private inner class ViewManagerUnderTest : ViewManager?>() { override fun getName(): String { return "SomeView" @@ -46,64 +46,40 @@ class ReactPropConstantsTest { Assertions.fail("This method should not be executed as a part of this test") } - @ReactProp(name = "boolProp") - fun setBoolProp(v: View?, value: Boolean) { - } + @ReactProp(name = "boolProp") fun setBoolProp(v: View?, value: Boolean) {} - @ReactProp(name = "intProp") - fun setIntProp(v: View?, value: Int) { - } + @ReactProp(name = "intProp") fun setIntProp(v: View?, value: Int) {} - @ReactProp(name = "floatProp") - fun setFloatProp(v: View?, value: Float) { - } + @ReactProp(name = "floatProp") fun setFloatProp(v: View?, value: Float) {} - @ReactProp(name = "doubleProp") - fun setDoubleProp(v: View?, value: Double) { - } + @ReactProp(name = "doubleProp") fun setDoubleProp(v: View?, value: Double) {} - @ReactProp(name = "stringProp") - fun setStringProp(v: View?, value: String?) { - } + @ReactProp(name = "stringProp") fun setStringProp(v: View?, value: String?) {} - @ReactProp(name = "boxedBoolProp") - fun setBoxedBoolProp(v: View?, value: Boolean?) { - } + @ReactProp(name = "boxedBoolProp") fun setBoxedBoolProp(v: View?, value: Boolean?) {} - @ReactProp(name = "boxedIntProp") - fun setBoxedIntProp(v: View?, value: Int?) { - } + @ReactProp(name = "boxedIntProp") fun setBoxedIntProp(v: View?, value: Int?) {} - @ReactProp(name = "arrayProp") - fun setArrayProp(v: View?, value: ReadableArray?) { - } + @ReactProp(name = "arrayProp") fun setArrayProp(v: View?, value: ReadableArray?) {} - @ReactProp(name = "mapProp") - fun setMapProp(v: View?, value: ReadableMap?) { - } + @ReactProp(name = "mapProp") fun setMapProp(v: View?, value: ReadableMap?) {} @ReactPropGroup(names = ["floatGroupPropFirst", "floatGroupPropSecond"]) - fun setFloatGroupProp(v: View?, index: Int, value: Float) { - } + fun setFloatGroupProp(v: View?, index: Int, value: Float) {} @ReactPropGroup(names = ["intGroupPropFirst", "intGroupPropSecond"]) - fun setIntGroupProp(v: View?, index: Int, value: Int) { - } + fun setIntGroupProp(v: View?, index: Int, value: Int) {} @ReactPropGroup(names = ["boxedIntGroupPropFirst", "boxedIntGroupPropSecond"]) - fun setBoxedIntGroupProp(v: View?, index: Int, value: Int?) { - } + fun setBoxedIntGroupProp(v: View?, index: Int, value: Int?) {} @ReactProp(name = "customIntProp", customType = "date") - fun customIntProp(v: View?, value: Int) { - } + fun customIntProp(v: View?, value: Int) {} @ReactPropGroup( - names = ["customBoxedIntGroupPropFirst", "customBoxedIntGroupPropSecond"], - customType = "color" - ) - fun customIntGroupProp(v: View?, index: Int, value: Int?) { - } + names = ["customBoxedIntGroupPropFirst", "customBoxedIntGroupPropSecond"], + customType = "color") + fun customIntGroupProp(v: View?, index: Int, value: Int?) {} } @Test @@ -111,41 +87,41 @@ class ReactPropConstantsTest { val viewManagers = listOf>(ViewManagerUnderTest()) val reactContext = ReactApplicationContext(RuntimeEnvironment.getApplication()) val uiManagerModule = UIManagerModule(reactContext, viewManagers, 0) - val constants: Map<*, *>? = valueAtPath(uiManagerModule.constants, "SomeView", "NativeProps") + val constants: Map<*, *> = + valueAtPath(uiManagerModule.constants as Map<*, *>, "SomeView", "NativeProps") Assertions.assertThat(constants) - .isEqualTo( - MapBuilder.builder() - .put("boolProp", "boolean") - .put("intProp", "number") - .put("doubleProp", "number") - .put("floatProp", "number") - .put("stringProp", "String") - .put("boxedBoolProp", "boolean") - .put("boxedIntProp", "number") - .put("arrayProp", "Array") - .put("mapProp", "Map") - .put("floatGroupPropFirst", "number") - .put("floatGroupPropSecond", "number") - .put("intGroupPropFirst", "number") - .put("intGroupPropSecond", "number") - .put("boxedIntGroupPropFirst", "number") - .put("boxedIntGroupPropSecond", "number") - .put("customIntProp", "date") - .put("customBoxedIntGroupPropFirst", "color") - .put("customBoxedIntGroupPropSecond", "color") - .build() - ) + .isEqualTo( + MapBuilder.builder() + .put("boolProp", "boolean") + .put("intProp", "number") + .put("doubleProp", "number") + .put("floatProp", "number") + .put("stringProp", "String") + .put("boxedBoolProp", "boolean") + .put("boxedIntProp", "number") + .put("arrayProp", "Array") + .put("mapProp", "Map") + .put("floatGroupPropFirst", "number") + .put("floatGroupPropSecond", "number") + .put("intGroupPropFirst", "number") + .put("intGroupPropSecond", "number") + .put("boxedIntGroupPropFirst", "number") + .put("boxedIntGroupPropSecond", "number") + .put("customIntProp", "date") + .put("customBoxedIntGroupPropFirst", "color") + .put("customBoxedIntGroupPropSecond", "color") + .build()) } companion object { - private fun valueAtPath(nestedMap: Map?, vararg keyPath: String): Map<*, *>? { - Assertions.assertThat(keyPath).isNotEmpty - var value = nestedMap + private fun valueAtPath(nestedMap: Map<*, *>, vararg keyPath: String): Map<*, *> { + require(keyPath.isNotEmpty()) { "keyPath must not be empty" } + var value: Map<*, *> = nestedMap for (key in keyPath) { - Assertions.assertThat(value).isInstanceOf(Map::class.java) - Assertions.assertThat(value).containsKey(key) - value = (value?.get(key) as? Map ?: error("Value not found for key: $key")) + require(key in value) { "Key '$key' not found in the map" } + require(value[key] is Map<*, *>) { "Key '$key' must be a map itself" } + value = value[key] as Map<*, *> } return value } From 004650634030ea6d6078915fdfe5c7d53692229d Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 17 Aug 2023 12:17:28 +0100 Subject: [PATCH 3/3] Update packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java --- .../com/facebook/react/uimanager/ViewManagersPropertyCache.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java index f177d882973875..c97c53e32848c1 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java @@ -394,7 +394,7 @@ public BoxedColorPropSetter(ReactProp prop, Method setter) { /*package*/ static Map getNativePropSettersForShadowNodeClass( Class cls) { if(cls == null) { - return new HashMap<>(); + return EMPTY_PROPS_MAP; }; for (Class iface : cls.getInterfaces()) {