Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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<View, ReactShadowNode<*>>() {
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<out ReactShadowNode<*>> = 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<String>(), any<String>()) }, never())
}
}
}
Loading