Skip to content
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

[grid] Only ignore extension caps with object/array values #14485

Open
wants to merge 1 commit into
base: trunk
Choose a base branch
from

Conversation

sbabcoc
Copy link
Contributor

@sbabcoc sbabcoc commented Sep 10, 2024

User description

Description

The existing handling of extension capabilities assigns special significance to four recognized prefixes: goog:, moz:, ms:, and se:. Capabilities with these prefixes are entirely ignored by the current default slot matcher implementation, but custom prefixes are considered, as well as those for Safari and Appium. This inconsistency means that properties in extension capabilities with complex values (objects and arrays) can cause affected newSession requests to fail even though extension capabilities are supposed to be vendor-specific and should probably not be evaluated by the default slot matcher.

As a compromise, this PR eliminates the "special" status of the four recognized prefixes, opting instead to ignore all extension capabilities with complex values. This maintains the existing behavior regarding the Options objects of Chrome, Firefox, and Edge while allowing node configurations and newSession requests to include extension capabilities that won't affect slot matching.

Motivation and Context

Revolves #14461

Note that this is technically a breaking change, because it revises the default slot matcher to consider extension capabilities with simple values that would previously have been ignored for the "special" prefixes, and the matcher will now ignore extension capabilities with complex values that would previously have been considered for "non-special" prefixes.
However, I'm unaware of any current use cases that will be adversely affected by this change.

The strategy employed by the PR is that extension capabilities which should be considered for matching can be expressed as capabilities with simple values and those which should be ignored can be specified as map elements or list items. This is a generalization of existing patterns from current use cases:

  • The "special" extension prefixes for browser options are expressed as maps, and these are currently ignored for purposes of slot matching.
  • The extension capabilities for Appium are expressed as simple values, and these are currently considered for purposes of slot matching.

Recommended slot matcher enhancements

To reduce the need for custom slot matchers, we could extend the WebDriverInfo interface to add a new method that vendors could implement to evaluate their own criteria:

Boolean matches(Capabilities stereotype, Capabilities capabilities);

The default implementation would return null to indicate that no evaluation has been performed. If the vendor chooses to implement the matches method, their initial step must be to invoke their own isSupporting method, returning null if the corresponding driver doesn't support the specified capabilities.

The implementation in DefaultSlotMatcher would be updated to iterate over the available WebDriverInfo providers, retaining only those whose isSupporting methods return true. The matches methods of this filtered list of providers will then be applied to the available nodes to determine which of these satisfies the criteria specified by the client request. The nodes that satisfy these evaluations (if any) would then be evaluated against the remaining common criteria.

Another potential enhancement would be to enable vendor-specific matches methods to return a weighted integer result instead of a simple Boolean. This would enable best-match behavior. Perhaps this is getting too complicated, though.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist

  • I have read the contributing document.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

PR Type

Bug fix, Tests


Description

  • Removed the special status of certain extension capability prefixes in DefaultSlotMatcher, opting to ignore all extension capabilities with complex values.
  • Simplified the logic for matching capabilities by removing unnecessary checks and conditions.
  • Updated RelaySessionFactory to streamline session creation by removing redundant capability filtering.
  • Revised test cases in DefaultSlotMatcherTest to align with the new handling of extension capabilities, using maps for complex values.

Changes walkthrough 📝

Relevant files
Bug fix
DefaultSlotMatcher.java
Revise extension capabilities handling in DefaultSlotMatcher

java/src/org/openqa/selenium/grid/data/DefaultSlotMatcher.java

  • Removed special handling for specific extension capability prefixes.
  • Updated logic to ignore all extension capabilities with complex
    values.
  • Simplified capability matching logic.
  • +31/-51 
    RelaySessionFactory.java
    Simplify session creation in RelaySessionFactory                 

    java/src/org/openqa/selenium/grid/node/relay/RelaySessionFactory.java

  • Removed logic to filter out browserName when appium:app is present.
  • Simplified session creation process.
  • +0/-11   
    Tests
    DefaultSlotMatcherTest.java
    Update DefaultSlotMatcher tests for revised capability handling

    java/test/org/openqa/selenium/grid/data/DefaultSlotMatcherTest.java

  • Updated test cases to reflect changes in extension capability
    handling.
  • Used maps for complex capability values in tests.
  • +10/-8   

    💡 PR-Agent usage:
    Comment /help on the PR to get a list of all available PR-Agent tools and their descriptions

    Copy link
    Contributor

    PR Reviewer Guide 🔍

    ⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
    🧪 PR contains tests
    🔒 No security concerns identified
    ⚡ Key issues to review

    Potential Performance Issue
    The new implementation of initialMatch method uses reduce(Boolean::logicalAnd) which may not short-circuit on the first false condition. Consider using allMatch for better performance.

    Possible Bug
    The extensionCapabilitiesMatch method now ignores complex objects and arrays, which might lead to unintended matching behavior for certain extension capabilities.

    @sbabcoc sbabcoc force-pushed the pr/revise-extension-capabilities-handling branch from a82c998 to 14999b7 Compare September 10, 2024 18:15
    Copy link
    Contributor

    PR Code Suggestions ✨

    CategorySuggestion                                                                                                                                    Score
    Possible issue
    Improve precision in matching complex capability values

    Consider using a more specific check for complex objects instead of returning true
    for all non-String, non-Number, and non-Boolean values. This could prevent
    unintended matches for complex objects.

    java/src/org/openqa/selenium/grid/data/DefaultSlotMatcher.java [144-148]

     if (capabilities.getCapability(name) instanceof Number ||
    -  capabilities.getCapability(name) instanceof Boolean) {
    +  capabilities.getCapability(name) instanceof Boolean ||
    +  capabilities.getCapability(name) instanceof Map) {
       return Objects.equals(stereotype.getCapability(name), capabilities.getCapability(name));
     }
    -return true;
    +return false;
     
    • Apply this suggestion
    Suggestion importance[1-10]: 8

    Why: This suggestion enhances the precision of the matching logic by returning false for unsupported complex objects, preventing unintended matches and improving the accuracy of the matching process.

    8
    Enhancement
    Improve handling of complex capability values in extension capabilities matching

    Consider using Map.of() or ImmutableMap.of() instead of creating a new HashMap for
    the complex capability values. This can make the code more concise and potentially
    more efficient.

    java/src/org/openqa/selenium/grid/data/DefaultSlotMatcher.java [144-148]

     if (capabilities.getCapability(name) instanceof Number ||
    -  capabilities.getCapability(name) instanceof Boolean) {
    +  capabilities.getCapability(name) instanceof Boolean ||
    +  capabilities.getCapability(name) instanceof Map) {
       return Objects.equals(stereotype.getCapability(name), capabilities.getCapability(name));
     }
     return true;
     
    • Apply this suggestion
    Suggestion importance[1-10]: 7

    Why: The suggestion correctly identifies the need to handle complex capability values like Maps, which aligns with the changes in the test cases. This improves the robustness of the capability matching logic.

    7
    Add logging for skipped extension capabilities to improve debugging

    Consider adding a log statement when skipping extension capabilities to improve
    debugging and traceability of the matching process.

    java/src/org/openqa/selenium/grid/data/DefaultSlotMatcher.java [88]

    -.filter(name -> !name.contains(":"))
    +.filter(name -> {
    +  boolean isExtensionCapability = name.contains(":");
    +  if (isExtensionCapability) {
    +    LOG.fine("Skipping extension capability: " + name);
    +  }
    +  return !isExtensionCapability;
    +})
     
    • Apply this suggestion
    Suggestion importance[1-10]: 6

    Why: Adding logging for skipped extension capabilities can aid in debugging and understanding the matching process, though it is not critical for functionality.

    6
    Best practice
    Use a constant for the extension capability separator to improve code maintainability

    Consider using a constant for the ":" character used to identify extension
    capabilities. This would improve maintainability and reduce the risk of typos.

    java/src/org/openqa/selenium/grid/data/DefaultSlotMatcher.java [88]

    -.filter(name -> !name.contains(":"))
    +private static final String EXTENSION_CAPABILITY_SEPARATOR = ":";
     
    +// In the method:
    +.filter(name -> !name.contains(EXTENSION_CAPABILITY_SEPARATOR))
    +
    • Apply this suggestion
    Suggestion importance[1-10]: 5

    Why: Using a constant for the separator improves maintainability and reduces the risk of errors, but it is a minor improvement in terms of code quality.

    5

    @sbabcoc sbabcoc force-pushed the pr/revise-extension-capabilities-handling branch from 14999b7 to dbe50be Compare September 10, 2024 18:41
    .filter(name -> !"platformName".equalsIgnoreCase(name))
    .map(
    name -> {
    if (capabilities.getCapability(name) instanceof String) {
    Copy link
    Contributor Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    The implementation of this conditional fails to consider that the corresponding stereotype capability could have a non-String value (including null), which would produce unexpected behavior. The revised implementation will only perform the case-insensitive comparison if the capability in the session request and the node stereotype both have String values.

    .orElse(true);
    .filter(name -> name.contains(":"))
    .filter(name -> capabilities.getCapability(name) != null)
    .map(name -> {
    Copy link
    Contributor Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    This revised comparison will only evaluate values that are strings, numbers, or Booleans; complex values (objects and arrays) are ignored. The prior "special" treatment of four recognized extension capability prefixes (goog:, moz:, ms:, and se:) has been replaced by this consistent handling of all extension capabilities.

    @@ -149,15 +147,6 @@ public Either<WebDriverException, ActiveSession> apply(CreateSessionRequest sess
    "New session request capabilities do not " + "match the stereotype."));
    }

    // remove browserName capability if 'appium:app' is present as it breaks appium tests when app
    Copy link
    Contributor Author

    @sbabcoc sbabcoc Sep 10, 2024

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    The merge operation that necessitated this special-case handling has been eliminated. The session factory shouldn't be altering clients' requested capabilities.

    @sbabcoc sbabcoc force-pushed the pr/revise-extension-capabilities-handling branch 2 times, most recently from ac4802b to 2d9609b Compare September 10, 2024 19:39
    "amsterdam",
    "ms:fruit",
    "mango");
    "food:dairy",
    Copy link
    Contributor Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    This test has been updated to verify the revised behavior, in which all extension capabilities with complex values are ignored for purposes of node matching.

    Copy link
    Contributor

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    I think it is a good idea to create a new test and let the existing test be as it is to verify the changes don't cause a breaking change for the users.

    Copy link
    Contributor Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    I restored the original test, altering the method name and inverting the expectation to correspond with the revised behavior.

    @@ -148,7 +149,7 @@ boolean isDownloadEnabled(WebDriverInfo driver, String customMsg) {
    reported.add(caps);
    return Collections.singleton(HelperFactory.create(config, caps));
    });
    String expected = driver.getDisplayName();
    String expected = "Edge".equals(driver.getDisplayName()) ? Browser.EDGE.browserName() : driver.getDisplayName();
    Copy link
    Contributor Author

    @sbabcoc sbabcoc Sep 10, 2024

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    This test was broken when I got here. For Microsoft Edge, the display name is Edge, but the browser name is MicrosoftEdge. Consequently, the expected value would never be matched.

    @sbabcoc sbabcoc force-pushed the pr/revise-extension-capabilities-handling branch from 2d9609b to cca2c28 Compare September 11, 2024 06:48
    "amsterdam",
    "ms:fruit",
    "mango");
    "food:dairy",
    Copy link
    Contributor

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    I think it is a good idea to create a new test and let the existing test be as it is to verify the changes don't cause a breaking change for the users.

    @sbabcoc sbabcoc force-pushed the pr/revise-extension-capabilities-handling branch from d5aeb2e to 84b6795 Compare September 16, 2024 21:35
    @sbabcoc sbabcoc force-pushed the pr/revise-extension-capabilities-handling branch 9 times, most recently from 6a5663f to d993e8c Compare September 22, 2024 19:54
    @sbabcoc sbabcoc force-pushed the pr/revise-extension-capabilities-handling branch from d993e8c to 378cb93 Compare September 27, 2024 05:42
    @sbabcoc sbabcoc force-pushed the pr/revise-extension-capabilities-handling branch 2 times, most recently from a48362c to e3253b5 Compare October 1, 2024 04:14
    @sbabcoc sbabcoc force-pushed the pr/revise-extension-capabilities-handling branch 3 times, most recently from 7f259f9 to 11bd7d3 Compare October 7, 2024 17:23
    @sbabcoc sbabcoc force-pushed the pr/revise-extension-capabilities-handling branch from 11bd7d3 to a46bb83 Compare October 15, 2024 22:07
    @VietND96 VietND96 changed the title Only ignore extension caps with object/array values [grid] Only ignore extension caps with object/array values Oct 25, 2024
    @sbabcoc sbabcoc force-pushed the pr/revise-extension-capabilities-handling branch 2 times, most recently from b29674d to 5e7e1ab Compare October 29, 2024 03:51
    @pujagani
    Copy link
    Contributor

    @diemol @krmahadevan Please help review this when time permits!

    @sbabcoc sbabcoc force-pushed the pr/revise-extension-capabilities-handling branch from ee1fc8f to 88ebc67 Compare November 2, 2024 04:37
    @sbabcoc sbabcoc force-pushed the pr/revise-extension-capabilities-handling branch from 88ebc67 to 5a9b636 Compare November 3, 2024 05:43
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Projects
    None yet
    Development

    Successfully merging this pull request may close these issues.

    3 participants