Status Update
Comments
pa...@google.com <pa...@google.com>
je...@google.com <je...@google.com> #2
First of all thanks for this detailed issue.
This issue had been investigated thoroughly when it was first reported internally. The surprising detail in this report is that the issue is not reproducible before 1.7
. I will look into this.
The main problem with POBox is the fact that it is deprecated. Since 2021 Sony has been shipping new Xperia devices with Gboard pre-installed. Although we are aware that there is still a considerable amount of users still using POBox, the described behavior is caused by POBox's noncompliant behavior with InputConnection
and InputMethodManager
documentation. However, this is understandable since TextView
implementation was also not respecting the behavior that is expected from Editors.
Ultimately we have decided to enforce the documented behavior with specifically regards to when editors should call InputMethodManager.updateSelection
. Also, although unconfirmed, there were traces of possible custom code being included in Sony OEM images that changed how InputMethodManager was notified from TextView. If POBox also depended on something like this, it would be impossible for Compose code to replicate the same unknown behavior.
an...@google.com <an...@google.com> #3
Or is that option not available?
Even if the root cause is POBox, from the perspective of the app's customers, it looks like an app bug, so this issue is a blocker against updating Jetpack Compose.
ra...@google.com <ra...@google.com> #4
Just to be sure, it is dangerous to replace Compose TextField with Android View EditText as a workaround for this issue.
Compose 1.7 has a bug that causes ANR when the focus is on EditText.
Another View-related bug in Compose 1.7 is that an Android View is focused by calling FocusManager.clearFocus().
Perhaps there is a lack of testing of Compose 1.7 in combination with Android View. There is also a possibility that there are other fatal bugs related to View.
In other words, the only options for apps targeting the Japanese market that require POBox support are to continue using Compose 1.6 or to use EditText in combination with various workarounds.
je...@google.com <je...@google.com> #5
Project: platform/frameworks/support
Branch: androidx-main
Author: Halil Ozercan <
Link:
Fix POBox keyboard issue
Expand for full commit details
Fix POBox keyboard issue
Fix: 373743376
Fix: 329209241
Test: NullableInputConnectionWrapperTest
Change-Id: I94e0e598274fb88b255f977f9fbd50dfbbb1ecb1
Files:
- M
compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/text/input/NullableInputConnectionWrapperTest.kt
- M
compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/NullableInputConnectionWrapper.android.kt
Hash: 57f58c4b80d5d8470b2aca325dfdcd55f235231e
Date: Thu Oct 24 01:25:20 2024
an...@google.com <an...@google.com> #6
Many thanks again for this report. Especially for giving us a huge clue in terms of what could be going wrong. The fix is now merged and I will ask for a cherry-pick into a stable release.
ra...@google.com <ra...@google.com> #7
Do you have any concrete plan to cherry-pick the fix into current stable version (1.7.x)? We are currently waiting it.
je...@google.com <je...@google.com> #8
Yes, this fix is planned to be included in a future 1.7.x
release.
cl...@google.com <cl...@google.com> #9
Thanks for the fix. Sorry to follow up on this. is it possible for you to share specific release version/date for the stable version? We are waiting on this to decide on our direction.
an...@google.com <an...@google.com> #10
an...@google.com <an...@google.com> #11
Update: there are also usecases when users want to know the amount of visible children to assert it with the expected one. The current workaround could be something like:
rule.onAllNodesWithText("item", substring = true)
.filter(isPlaced())
.assertCountEquals(1)
...
fun isPlaced() = SemanticsMatcher("isPlaced") { node -> node.layoutInfo.isPlaced }
But we need to provide an official api for this use case as well
an...@google.com <an...@google.com>
rh...@gmail.com <rh...@gmail.com> #12
Can we ask what is the use case for returning these cached nodes?
They do not affect the UI thus it seems we do not need to assert on these cached nodes when testing the UI.
With each Compose update more and more optimizations are being put into these Lazy type views.
Thus it's causing our otherwise accurate UI tests to break more and more.
Our team is confused as to why we have this complicated workaround code for simple assertions.
Our workaround
We overrode every onNode
* method we used to filter out these very unexpected nodes.
Extension Methods
// region Cache Patch
/** A patched version of [ComposeTestRule.onNode] that filters out cached views from lazy views. */
fun ComposeTestRule.onNodePatched(matcher: SemanticsMatcher) =
onNode(matcher and isNotCached())
/** A patched version of [ComposeTestRule.onNodeWithText] that filters out cached views from lazy views. */
fun ComposeTestRule.onNodeWithTextPatched(text: String, substring: Boolean = false, ignoreCase: Boolean = false, useUnmergedTree: Boolean = false) =
onAllNodesWithText(text = text, substring = substring, ignoreCase = ignoreCase, useUnmergedTree = useUnmergedTree)
.filterToOneNotCached()
/** A patched version of [ComposeTestRule.onNodeWithTag] that filters out cached views from lazy views. */
fun ComposeTestRule.onNodeWithTagPatched(testTag: String, useUnmergedTree: Boolean = false) =
onAllNodesWithTag(testTag = testTag, useUnmergedTree = useUnmergedTree)
.filterToOneNotCached()
/** A patched version of [ComposeTestRule.onNodeWithContentDescription] that filters out cached views from lazy views. */
fun ComposeTestRule.onNodeWithContentDescriptionPatched(label: String) =
onAllNodesWithContentDescription(label = label)
.filterToOneNotCached()
/** A patched version of [ComposeTestRule.onAllNodesWithText] that filters out cached views from lazy views. */
fun ComposeTestRule.onAllNodesWithTextPatched(text: String) =
onAllNodesWithText(text = text)
.filterOutCached()
/** A patched version of [ComposeTestRule.onAllNodesWithContentDescription] that filters out cached views from lazy views. */
fun ComposeTestRule.onAllNodesWithContentDescriptionPatched(label: String) =
onAllNodesWithContentDescription(label = label)
.filterOutCached()
/** Filters out cached views from lazy views and expects 1 or 0 results. */
fun SemanticsNodeInteractionCollection.filterToOneNotCached() =
filterToOne(isNotCached())
/** Filters out cached views from lazy views and expects 0 or more results. */
fun SemanticsNodeInteractionCollection.filterOutCached() =
filter(isNotCached())
/**
* Matches against only nodes that are not "cached" by lazy lists. This allows us to filter out
* nodes that do not appear in the UI but are reported by Compose's testing framework. These cached
* nodes cause issues because they will cause assertIsDisplayed to fail due to 2 nodes with the same
* values are reportedly displayed, but one is displayed and the other is cached. Cached nodes also
* cause assertDoesNotExist to fail because the cached node that does not exist on the UI does exist
* according to the test framework.
*
* This matcher is used in the methods above to globally filter out these very unexpected nodes.
* We hope Compose stops reporting these cached nodes in a future update and we can remove this patch.
*
* https://issuetracker.google.com/issues/187188981
*/
fun isNotCached() = SemanticsMatcher("isNotCached") { node -> node.layoutInfo.isPlaced }
// endregion
an...@google.com <an...@google.com> #13
Sorry about that. Agree there are no good reasons to return such cached nodes in most cases. Basically it is only really needed when you test this exact caching logic (some of our lazy layout related tests do that).
I believe they should be filtered out by default with some extra api allowing to get the unfiltered list of nodes. This bug will be dedicated to this work.
ae...@google.com <ae...@google.com> #14
At this stage I think we should only be making assertIsDisplayed
/assertIsNotDisplayed
monotonically more lenient, as they're so widely used that making either of them more strict is likely to break real-world tests and prevent people from upgrading Compose.
The way they are exact inverse of each other means we cannot make one of them more lenient without making the other one more strict. So in WIP patch strict: Boolean
parameter to allow their behavior to start to diverge. I suggest the same approach could be used to make assertIsNotDisplayed
more lenient to fix this issue.
ks...@gmail.com <ks...@gmail.com> #15
Hello team, we've recently encountered this issue while upgrading from Compose 1.4.3 to Compose 1.5.0-beta03. (I couldn't test against the stable version of Compose 1.5 due to AGP and compileSdkVersion requirements, but I haven't found any relevant fixes between the 1.5.0-beta03 and the stable 1.5.0 release.)
For example, the issue manifests in the following ways:
assertDoesNotExist
fails because a cached node still exists.assertIsDisplayed
fails because the test framework detects more than one displayed node, including cached nodes.- Tests checking the node count within the tree report inaccurate results due to cached nodes.
Could you please take a look to investigate whether this constitutes a regression? While the workaround with the node placement check (e.g.,
me...@thomaskeller.biz <me...@thomaskeller.biz> #16
This is a very serious regression that hit me as well when updating to a newer Compose version. IMHO the new optimization is not even dependent on the runtime version of the library, but was somehow introduced in the new Compose Compiler version 1.5.3, which needs to be used when the project's Kotlin version is updated to 1.9.10, because when I downgrade the runtime version of compose to my previous version (BOM 2023.04.01) the error persists.
Please, fix this issue as it's hard to figure out why LazyRow / LazyColumn testing suddenly breaks for no obvious reason.
ks...@gmail.com <ks...@gmail.com> #17
It does look like it is a regression or at least a significant behavior change introduced in Compose 1.5. Although there was no response/acknowledgment to this FR, I found
ap...@google.com <ap...@google.com> #18
Branch: androidx-main
commit 78d5dc8c9b42c32b0c8518c72181c19620a71c05
Author: Andrey Kulikov <andreykulikov@google.com>
Date: Thu Oct 12 18:29:24 2023
Filter out the deactivated semantic nodes by default and introduce assertIsDeactivated()
The children of SubcomposeLayout which are retained to be reused in future are considered deactivated. We were returning them along with the active nodes in all the semantics api which was hard to reason about as from the perspective of the users such nodes are not visible. Now we will filter out them by default, plus we introduce new APIs to be able to assert that the node is deactivated.
Test: modified affected tests to use the new function instead
Bug: 187188981
Bug: 302117973
Relnote: The children of SubcomposeLayout (and layouts like LazyColumn based on it) which are retained to be reused in future are considered deactivated. New assertIsDeactivated() test API was introduced to test such nodes. The rests of the test apis will filter out deactivated nodes by default.
Change-Id: I2ef84fb2ed578238bb20c07655c475df6fb8dbd0
M compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridSlotsReuseTest.kt
M compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/lazy/list/LazyListSlotsReuseTest.kt
M compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/lazy/list/LazyNestedScrollingTest.kt
M compose/ui/ui-test/api/current.txt
M compose/ui/ui-test/api/restricted_current.txt
M compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction.kt
M compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TestOwner.kt
M compose/ui/ui/api/current.txt
M compose/ui/ui/api/restricted_current.txt
M compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LayoutInfo.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsOwner.kt
M paging/paging-compose/src/androidInstrumentedTest/kotlin/androidx/paging/compose/LazyPagingItemsTest.kt
M tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/grid/LazyGridSlotsReuseTest.kt
M tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListSlotsReuseTest.kt
an...@team.bumble.com <an...@team.bumble.com> #19
an...@google.com <an...@google.com> #20
Filed separate bug
And
ap...@google.com <ap...@google.com> #21
Branch: androidx-main
commit df7e5823b5e0ad1a1da88a71b44708597848769f
Author: Andrey Kulikov <andreykulikov@google.com>
Date: Wed Dec 20 14:18:39 2023
Filter out deactivated nodes from SemanticNodes.children
In aosp/2787059 we started filtering out the deactivated nodes so users can't see them in their tests. However one use case was missed:
rule.onNodeWithTag("list")
.onChildren().assertCountEquals(expectedCount)
This code will still see the deactivated nodes as we are not filtering out deactivated nodes in the list returned by SemanticNode.children.
Test: new test in SubcomposeLayoutTest
Bug: 187188981
Bug: 305905580
Fixes: 317202262
Change-Id: I4fd3d6248fc5a24bdadfdd5c60afe31386fcf7d7
M compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsNode.kt
M compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsOwner.kt
be...@gmail.com <be...@gmail.com> #22
Thank you very much for providing this fix!
So do I understand that right, to make sure that a node does not exist or is not displayed, we can use assertIsDeactivated()
? So assertIsDeactivated()
basically means assertDoesNotExistOrIsNotDisplayed()
?
an...@google.com <an...@google.com> #23
assertIsNotDisplayed() is still needed for nodes, which are not deactivated, but not displayed because it is composed, but not placed, or clipped.
na...@google.com <na...@google.com> #24
The following release(s) address this bug.It is possible this bug has only been partially addressed:
androidx.tv:tv-foundation:1.0.0-alpha11
Description
Initially this bug was only about allowing to assert that node is not visible for any reason In my use case I don't care for what reason the node is not displayed. Either it is because the node is not composed at all, or it was composed, but wasn't placed. This behaviour in LazyColumn is not guaranteed as prefetching can decide to precompose an item in advance. But currently we have
Aside from that we should have a solid strategy on how we work with deactivated items(the ones from lazy list reuse pool):