-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Increase testing coverage and maintainability of android manifest parsing logic #174070
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Increase testing coverage and maintainability of android manifest parsing logic #174070
Conversation
…ut of utils into task
… and add unit tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request is a great improvement. It refactors the Android manifest parsing logic for deeplinks into a dedicated, modern Gradle task, which significantly enhances maintainability and testability. The addition of a comprehensive test suite with a manifest builder is also excellent. My feedback focuses on further improving maintainability by leveraging Kotlin's features for immutability in your data classes, which is a best practice and aligns with the goals of this PR.
| class IntentFilterCheck( | ||
| var hasAutoVerify: Boolean = false, | ||
| var hasActionView: Boolean = false, | ||
| var hasDefaultCategory: Boolean = false, | ||
| var hasBrowsableCategory: Boolean = false | ||
| ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This class can be converted to a data class with immutable val properties. This improves maintainability and makes instances of this class truly immutable.1
This change would require adjusting the instantiation logic in DeepLinkJsonFromManifestTaskHelper.kt to gather all boolean values before creating the IntentFilterCheck instance, which would also be an improvement.
| class IntentFilterCheck( | |
| var hasAutoVerify: Boolean = false, | |
| var hasActionView: Boolean = false, | |
| var hasDefaultCategory: Boolean = false, | |
| var hasBrowsableCategory: Boolean = false | |
| ) { | |
| data class IntentFilterCheck( | |
| val hasAutoVerify: Boolean = false, | |
| val hasActionView: Boolean = false, | |
| val hasDefaultCategory: Boolean = false, | |
| val hasBrowsableCategory: Boolean = false | |
| ) { |
Style Guide References
Footnotes
-
The Android Kotlin Style Guide recommends preferring
valovervarfor properties that are not reassigned after initialization to ensure immutability. Using adata classalso provides useful generated methods likeequals(),hashCode(), andtoString(). ↩
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there some reason not to take this change?
| val intentFilterCheck = IntentFilterCheck() | ||
| if (appLinkIntent.attribute("android:autoVerify") == MANIFEST_VALUE_TRUE) { | ||
| intentFilterCheck.hasAutoVerify = true | ||
| } | ||
|
|
||
| val actionItems: List<Node> = | ||
| appLinkIntent.children().filterIsInstance<Node>().filter { item -> | ||
| item.name() == "action" | ||
| } | ||
|
|
||
| intentFilterCheck.hasActionView = | ||
| actionItems.any { action -> | ||
| action.attribute(MANIFEST_NAME_KEY) == "android.intent.action.VIEW" | ||
| } | ||
| val categoryItems: List<Node> = | ||
| appLinkIntent.children().filterIsInstance<Node>().filter { item -> | ||
| item.name() == "category" | ||
| } | ||
| intentFilterCheck.hasDefaultCategory = | ||
| categoryItems.any { category -> category.attribute(MANIFEST_NAME_KEY) == "android.intent.category.DEFAULT" } | ||
| intentFilterCheck.hasBrowsableCategory = | ||
| categoryItems.any { category -> category.attribute(MANIFEST_NAME_KEY) == "android.intent.category.BROWSABLE" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To support making IntentFilterCheck an immutable data class, you can refactor this block to first determine the values for its properties, and then create an immutable instance in a single call. This improves readability and maintainability by separating the logic for determining the checks from the object creation.1
val hasAutoVerify = appLinkIntent.attribute("android:autoVerify") == MANIFEST_VALUE_TRUE
val actionItems: List<Node> =
appLinkIntent.children().filterIsInstance<Node>().filter { item ->
item.name() == "action"
}
val hasActionView = actionItems.any { action ->
action.attribute(MANIFEST_NAME_KEY) == "android.intent.action.VIEW"
}
val categoryItems: List<Node> =
appLinkIntent.children().filterIsInstance<Node>().filter { item ->
item.name() == "category"
}
val hasDefaultCategory = categoryItems.any { category -> category.attribute(MANIFEST_NAME_KEY) == "android.intent.category.DEFAULT" }
val hasBrowsableCategory = categoryItems.any { category -> category.attribute(MANIFEST_NAME_KEY) == "android.intent.category.BROWSABLE" }
val intentFilterCheck = IntentFilterCheck(
hasAutoVerify = hasAutoVerify,
hasActionView = hasActionView,
hasDefaultCategory = hasDefaultCategory,
hasBrowsableCategory = hasBrowsableCategory
)Style Guide References
Footnotes
-
The Android Kotlin Style Guide recommends preferring
valovervarfor properties that are not reassigned after initialization to ensure immutability. ↩
camsim99
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks good to me but left some food for thought!
| class IntentFilterCheck( | ||
| var hasAutoVerify: Boolean = false, | ||
| var hasActionView: Boolean = false, | ||
| var hasDefaultCategory: Boolean = false, | ||
| var hasBrowsableCategory: Boolean = false | ||
| ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there some reason not to take this change?
| val appLinkSettings = DeepLinkJsonFromManifestTaskHelper.createAppLinkSettings(defaultNamespace, manifestFile) | ||
| assertEquals(defaultNamespace, appLinkSettings.applicationId) | ||
| assertFalse(appLinkSettings.deeplinkingFlagEnabled) | ||
| assertTrue(appLinkSettings.deeplinks.isEmpty()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Is the SAXParseException so unclear that it deserves a try/catch? Just curious because I'm a bit worried that someone may try to copy the try/catch pattern you use here, forget to not just specifically catch SAXParseExceptions, and then accidentally swallow assertion errors from the assertion tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it is super unhelpful and gives you basically nothing to debug with. I kept it focused on the specific exception that happens when the xml is invalid because the xml is being created by test tooling.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking of ways to make this more clear. I could duplicate the parse call from the helper in the "build" function such that when we called build it validated the xml was at least parsable. Then if it failed in the builder I could call fail and print. Do you like that modification?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like that structure better!
|
@camsim99 I ended up trying out #174070 (comment) and liked it a lot more. |
…nifest parsing logic (flutter/flutter#174070)
…nifest parsing logic (flutter/flutter#174070)
…nifest parsing logic (flutter/flutter#174070)
…nifest parsing logic (flutter/flutter#174070)
…nifest parsing logic (flutter/flutter#174070)
…nifest parsing logic (flutter/flutter#174070)
flutter/flutter@c65f01d...da5523a 2025-08-27 [email protected] [native assets] Roll dependencies (flutter/flutter#174522) 2025-08-27 [email protected] Roll Skia from 8cf2faada2b5 to 7e6da45059c5 (5 revisions) (flutter/flutter#174523) 2025-08-27 [email protected] [Android] Restrict AOT shared library engine flag to trusted paths (flutter/flutter#173359) 2025-08-27 [email protected] Roll Fuchsia Linux SDK from L5zGzsIWIS8N36AFQ... to bHYRvLv2Dg56RWZF2... (flutter/flutter#174518) 2025-08-27 [email protected] Roll Packages from 1ef712e to 86fbeec (7 revisions) (flutter/flutter#174521) 2025-08-27 [email protected] Roll Skia from 448a0d0e57e3 to 8cf2faada2b5 (1 revision) (flutter/flutter#174510) 2025-08-27 [email protected] Roll Skia from 4703976a4dae to 448a0d0e57e3 (3 revisions) (flutter/flutter#174494) 2025-08-27 [email protected] Fix SliverMainAxisGroup and SliverCrossAxisGroup gestures' local positions. (flutter/flutter#174265) 2025-08-27 [email protected] Fix lock up when window resized with merged UI and platform threads. (flutter/flutter#172893) 2025-08-27 [email protected] Roll Skia from dc2872de506f to 4703976a4dae (1 revision) (flutter/flutter#174489) 2025-08-27 [email protected] Roll Dart SDK from db45c0ce46f9 to 89023922f96d (2 revisions) (flutter/flutter#174481) 2025-08-27 [email protected] Migrate examples and defaults to `WidgetState` (flutter/flutter#174421) 2025-08-27 [email protected] Roll Skia from 8e48b9e6d67b to dc2872de506f (7 revisions) (flutter/flutter#174479) 2025-08-27 [email protected] SnackBar with action no longer auto-dismiss (flutter/flutter#173084) 2025-08-26 [email protected] Roll Dart SDK from 9054cd8af73c to db45c0ce46f9 (1 revision) (flutter/flutter#174438) 2025-08-26 [email protected] Roll Skia from f941bfab7c09 to 8e48b9e6d67b (4 revisions) (flutter/flutter#174468) 2025-08-26 [email protected] Fix bug in test_golden_comparator, add an e2e test. (flutter/flutter#174459) 2025-08-26 [email protected] fix typo in test_profile/README.md (flutter/flutter#174384) 2025-08-26 [email protected] Remove CP labels on not-merged PRs, and explain why (flutter/flutter#174448) 2025-08-26 [email protected] Increase testing coverage and maintainability of android manifest parsing logic (flutter/flutter#174070) 2025-08-26 [email protected] [web] Add test that pictures are not rasterized when clipped out (flutter/flutter#174452) 2025-08-26 [email protected] Roll Skia from 538260c45b4a to f941bfab7c09 (3 revisions) (flutter/flutter#174450) 2025-08-26 [email protected] fixes the vulkan image layout transitions for mipmap generation (flutter/flutter#173884) 2025-08-26 [email protected] Move flakey iOS tests to bringup (flutter/flutter#174446) 2025-08-26 [email protected] [Impeller] Make sure inline passes always do a clear action. (flutter/flutter#174083) 2025-08-26 [email protected] Roll Skia from 21214d63fc40 to 538260c45b4a (2 revisions) (flutter/flutter#174441) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC [email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
…nifest parsing logic (flutter/flutter#174070)
…sing logic (flutter#174070) Related to: flutter#166550 Follow up to flutter#173794 to implement suggested improvements and with strictly human authored tests changes. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing.
…sing logic (flutter#174070) Related to: flutter#166550 Follow up to flutter#173794 to implement suggested improvements and with strictly human authored tests changes. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing.
…sing logic (flutter#174070) Related to: flutter#166550 Follow up to flutter#173794 to implement suggested improvements and with strictly human authored tests changes. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing.
…nifest parsing logic (flutter/flutter#174070)
…sing logic (flutter#174070) Related to: flutter#166550 Follow up to flutter#173794 to implement suggested improvements and with strictly human authored tests changes. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing.
Related to: #166550
Follow up to #173794 to implement suggested improvements and with strictly human authored tests changes.
Pre-launch Checklist
///).