diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerPropertyUpdater.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerPropertyUpdater.kt index 7a33df4ae121..0b18da7c1b84 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerPropertyUpdater.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerPropertyUpdater.kt @@ -135,7 +135,9 @@ public object ViewManagerPropertyUpdater { @Suppress("DEPRECATION", "UNCHECKED_CAST") return setterClass.newInstance() as T } catch (e: ClassNotFoundException) { - FLog.w(TAG, "Could not find generated setter for $cls") + // A generated setter is optional: the annotation processor that produced them was removed, + // so reaching the reflection-based fallback is the expected path, not a problem. + FLog.d(TAG, "Could not find generated setter for $cls") return null } catch (e: InstantiationException) { throw RuntimeException("Unable to instantiate methods getter for $clsName", e) diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ViewManagerPropertyUpdaterTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ViewManagerPropertyUpdaterTest.kt new file mode 100644 index 000000000000..e9b8070ef233 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ViewManagerPropertyUpdaterTest.kt @@ -0,0 +1,63 @@ +/* + * 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.common.logging.FLog +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsForTests +import com.facebook.react.uimanager.annotations.ReactProp +import org.assertj.core.api.Assertions.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.mockStatic +import org.mockito.Mockito.never +import org.mockito.kotlin.any +import org.robolectric.RobolectricTestRunner + +/** Tests for the setter lookup done by [ViewManagerPropertyUpdater]. */ +@RunWith(RobolectricTestRunner::class) +class ViewManagerPropertyUpdaterTest { + @Suppress("DEPRECATION") + private class ViewManagerWithoutGeneratedSetter : ViewManager>() { + override fun getName() = "ViewManagerWithoutGeneratedSetter" + + override fun createShadowNodeInstance(): ReactShadowNode<*> = + error("This method should not be executed as a part of this test") + + override fun getShadowNodeClass(): Class> = ReactShadowNode::class.java + + override fun createViewInstance(reactContext: ThemedReactContext): View = + error("This method should not be executed as a part of this test") + + override fun prepareToRecycleView(reactContext: ThemedReactContext, view: View): View? = + error("This method should not be executed as a part of this test") + + override fun updateExtraData(view: View, extraData: Any) = + error("This method should not be executed as a part of this test") + + @ReactProp(name = "someProp") fun setSomeProp(view: View?, value: Boolean) = Unit + } + + @Before + fun setUp() { + ReactNativeFeatureFlagsForTests.setUp() + // Setters are cached per class, so the lookup would be skipped if an earlier test ran it. + ViewManagerPropertyUpdater.clear() + } + + @Test + fun missingGeneratedSetterFallsBackToReflectionWithoutWarning() { + mockStatic(FLog::class.java).use { flog -> + val nativeProps = ViewManagerWithoutGeneratedSetter().nativeProps + + assertThat(nativeProps).containsKey("someProp") + flog.verify({ FLog.w(any(), any()) }, never()) + } + } +}