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] Support Grid custom capability mutators #13672

Open
wants to merge 2 commits into
base: trunk
Choose a base branch
from

Conversation

Purus
Copy link

@Purus Purus commented Mar 10, 2024

User description

Custom capability mutators(with ordering priority) can be placed as part of Grid setup and will be used.

Fixes #13628

Thanks for contributing to Selenium!
A PR well described will help maintainers to quickly review and merge it

Before submitting your PR, please check our contributing guidelines.
Avoid large PRs, help reviewers by making them as simple and short as possible.

Description

Custom capability mutators(with ordering priority) can be placed as part of Grid setup and will be used.

Motivation and Context

Provides option to update the capabilities based on various aspects. Use cases available in #13628.

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.

Type

Enhancement


Description

  • Introduced a new CapabilitiesMutatorService to support custom capability mutations with ordering priority.
  • Defined a CapabilityMutator interface for implementing custom mutators.
  • Updated DriverServiceSessionFactory to use CapabilitiesMutatorService for mutating session capabilities.
  • Adjusted SessionCapabilitiesMutator to implement the new CapabilityMutator interface.
  • Added comprehensive tests for the new CapabilitiesMutatorService.
  • Updated Bazel build configuration to include necessary dependencies.

Changes walkthrough

Relevant files
Enhancement
CapabilitiesMutatorService.java
Introduce Capabilities Mutator Service for Custom Capability Mutations

java/src/org/openqa/selenium/grid/node/config/CapabilitiesMutatorService.java

  • Introduced a new service for mutating capabilities with custom
    mutators.
  • Custom mutators are loaded using ServiceLoader and sorted by order.
  • Provides a method to mutate capabilities based on the loaded custom
    mutators.
  • +64/-0   
    CapabilityMutator.java
    Define Capability Mutator Interface                                           

    java/src/org/openqa/selenium/grid/node/config/CapabilityMutator.java

    • Defined an interface for capability mutators with a default order.
    +29/-0   
    DriverServiceSessionFactory.java
    Use CapabilitiesMutatorService in DriverServiceSessionFactory

    java/src/org/openqa/selenium/grid/node/config/DriverServiceSessionFactory.java

  • Replaced SessionCapabilitiesMutator with CapabilitiesMutatorService
    for mutating session capabilities.
  • Adjusted imports and cleaned up the code.
  • +21/-40 
    SessionCapabilitiesMutator.java
    Update SessionCapabilitiesMutator to Implement CapabilityMutator

    java/src/org/openqa/selenium/grid/node/config/SessionCapabilitiesMutator.java

  • Made SessionCapabilitiesMutator implement the new CapabilityMutator
    interface.
  • +3/-7     
    Tests
    CapabilitiesMutatorServiceTest.java
    Add Tests for CapabilitiesMutatorService                                 

    java/test/org/openqa/selenium/grid/node/config/CapabilitiesMutatorServiceTest.java

  • Added tests for CapabilitiesMutatorService.
  • Tests cover default mutation, custom mutator application, and custom
    mutator order.
  • +112/-0 
    Configuration changes
    BUILD.bazel
    Add Core Dependency to Distributor Config BUILD                   

    java/src/org/openqa/selenium/grid/distributor/config/BUILD.bazel

  • Added dependency on //java/src/org/openqa/selenium:core for the
    distributor config.
  • +1/-0     

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

    Custom capability mutators(with ordering priority) can be placed as part of Grid setup and will be used.
    
    Fixes SeleniumHQ#13628
    @CLAassistant
    Copy link

    CLAassistant commented Mar 10, 2024

    CLA assistant check
    All committers have signed the CLA.

    Copy link

    PR Description updated to latest commit (124a3cc)

    Copy link

    PR Review

    ⏱️ Estimated effort to review [1-5]

    3, because the PR introduces a new feature with a moderate amount of new code across several files, including core functionality changes, interface definitions, and tests. The logic seems straightforward, but understanding the impact on the existing system and ensuring compatibility requires a thorough review.

    🧪 Relevant tests

    Yes

    🔍 Possible issues

    Thread Safety: The CapabilitiesMutatorService class uses a List<CapabilityMutator> for custom mutators which might be accessed by multiple threads. Ensure that access to this list is thread-safe.

    Error Handling: There is no visible error handling or logging in the CapabilitiesMutatorService when applying mutators. Consider adding error handling to catch and log potential issues during the mutation process.

    Dependency Injection: The use of ServiceLoader in CapabilitiesMutatorService to load CapabilityMutator implementations is a static method of discovery. This approach might limit the flexibility in configuring mutators dynamically at runtime. Consider if dependency injection could offer better configurability.

    🔒 Security concerns

    No


    ✨ Review tool usage guide:

    Overview:
    The review tool scans the PR code changes, and generates a PR review. The tool can be triggered automatically every time a new PR is opened, or can be invoked manually by commenting on any PR.
    When commenting, to edit configurations related to the review tool (pr_reviewer section), use the following template:

    /review --pr_reviewer.some_config1=... --pr_reviewer.some_config2=...
    

    With a configuration file, use the following template:

    [pr_reviewer]
    some_config1=...
    some_config2=...
    
    Utilizing extra instructions

    The review tool can be configured with extra instructions, which can be used to guide the model to a feedback tailored to the needs of your project.

    Be specific, clear, and concise in the instructions. With extra instructions, you are the prompter. Specify the relevant sub-tool, and the relevant aspects of the PR that you want to emphasize.

    Examples for extra instructions:

    [pr_reviewer] # /review #
    extra_instructions="""
    In the 'possible issues' section, emphasize the following:
    - Does the code logic cover relevant edge cases?
    - Is the code logic clear and easy to understand?
    - Is the code logic efficient?
    ...
    """
    

    Use triple quotes to write multi-line instructions. Use bullet points to make the instructions more readable.

    How to enable\disable automation
    • When you first install PR-Agent app, the default mode for the review tool is:
    pr_commands = ["/review", ...]
    

    meaning the review tool will run automatically on every PR, with the default configuration.
    Edit this field to enable/disable the tool, or to change the used configurations

    Auto-labels

    The review tool can auto-generate two specific types of labels for a PR:

    • a possible security issue label, that detects possible security issues (enable_review_labels_security flag)
    • a Review effort [1-5]: x label, where x is the estimated effort to review the PR (enable_review_labels_effort flag)
    Extra sub-tools

    The review tool provides a collection of possible feedbacks about a PR.
    It is recommended to review the possible options, and choose the ones relevant for your use case.
    Some of the feature that are disabled by default are quite useful, and should be considered for enabling. For example:
    require_score_review, require_soc2_ticket, and more.

    Auto-approve PRs

    By invoking:

    /review auto_approve
    

    The tool will automatically approve the PR, and add a comment with the approval.

    To ensure safety, the auto-approval feature is disabled by default. To enable auto-approval, you need to actively set in a pre-defined configuration file the following:

    [pr_reviewer]
    enable_auto_approval = true
    

    (this specific flag cannot be set with a command line argument, only in the configuration file, committed to the repository)

    You can also enable auto-approval only if the PR meets certain requirements, such as that the estimated_review_effort is equal or below a certain threshold, by adjusting the flag:

    [pr_reviewer]
    maximal_review_effort = 5
    
    More PR-Agent commands

    To invoke the PR-Agent, add a comment using one of the following commands:

    • /review: Request a review of your Pull Request.
    • /describe: Update the PR title and description based on the contents of the PR.
    • /improve [--extended]: Suggest code improvements. Extended mode provides a higher quality feedback.
    • /ask <QUESTION>: Ask a question about the PR.
    • /update_changelog: Update the changelog based on the PR's contents.
    • /add_docs 💎: Generate docstring for new components introduced in the PR.
    • /generate_labels 💎: Generate labels for the PR based on the PR's contents.
    • /analyze 💎: Automatically analyzes the PR, and presents changes walkthrough for each component.

    See the tools guide for more details.
    To list the possible configuration parameters, add a /config comment.

    See the review usage page for a comprehensive guide on using this tool.

    Copy link

    codiumai-pr-agent-pro bot commented Mar 10, 2024

    PR Code Suggestions

    CategorySuggestions                                                                                                                                                       
    Enhancement
    Use a thread-safe collection for customMutators.

    Consider using a thread-safe collection for customMutators since it might be accessed or
    modified by multiple threads simultaneously. You can use CopyOnWriteArrayList from the
    java.util.concurrent package to ensure thread safety.

    java/src/org/openqa/selenium/grid/node/config/CapabilitiesMutatorService.java [31]

    -private List<CapabilityMutator> customMutators;
    +private List<CapabilityMutator> customMutators = new CopyOnWriteArrayList<>();
     
    Performance
    Cache the comparator used for sorting custom mutators.

    To improve the efficiency of sorting custom mutators, consider caching the result of
    Comparator.comparingInt(CapabilityMutator::getOrder).reversed() as a static final field.
    This avoids creating a new Comparator instance each time the constructor is called.

    java/src/org/openqa/selenium/grid/node/config/CapabilitiesMutatorService.java [39]

    -.sorted(Comparator.comparingInt(CapabilityMutator::getOrder).reversed())
    +private static final Comparator<CapabilityMutator> MUTATOR_COMPARATOR = Comparator.comparingInt(CapabilityMutator::getOrder).reversed();
    +...
    +.sorted(MUTATOR_COMPARATOR)
     
    Maintainability
    Allow CapabilitiesMutatorService to be passed as a parameter.

    Instead of directly creating a new CapabilitiesMutatorService instance in the constructor,
    consider allowing it to be passed as a parameter. This would make your class more flexible
    and easier to test, as you could pass mock or stub implementations of
    CapabilitiesMutatorService.

    java/src/org/openqa/selenium/grid/node/config/DriverServiceSessionFactory.java [78]

    -this.capabilitiesMutatorService = new CapabilitiesMutatorService(this.stereotype);
    +public DriverServiceSessionFactory(
    +    Tracer tracer,
    +    Predicate<Capabilities> predicate,
    +    DriverService.Builder<?, ?> builder,
    +    Capabilities stereotype,
    +    CapabilitiesMutatorService capabilitiesMutatorService) {
    +  ...
    +  this.capabilitiesMutatorService = capabilitiesMutatorService;
    +}
     
    Extract custom mutators loading logic into a separate method.

    To enhance the readability and maintainability of your code, consider extracting the logic
    for loading and sorting custom mutators into a separate method. This keeps the constructor
    clean and makes the code easier to understand.

    java/src/org/openqa/selenium/grid/node/config/CapabilitiesMutatorService.java [36-40]

    -ServiceLoader<CapabilityMutator> loader = ServiceLoader.load(CapabilityMutator.class);
    -customMutators = StreamSupport.stream(loader.spliterator(), false)
    -  .sorted(Comparator.comparingInt(CapabilityMutator::getOrder).reversed())
    -  .collect(Collectors.toList());
    +private void loadCustomMutators() {
    +  ServiceLoader<CapabilityMutator> loader = ServiceLoader.load(CapabilityMutator.class);
    +  customMutators = StreamSupport.stream(loader.spliterator(), false)
    +    .sorted(Comparator.comparingInt(CapabilityMutator::getOrder).reversed())
    +    .collect(Collectors.toList());
    +}
    +...
    +loadCustomMutators();
     
    Best practice
    Validate input parameters of public methods.

    It's a good practice to validate the input parameters of public methods to avoid
    unexpected behavior or errors. Consider adding null checks for the Capabilities parameter
    in the getMutatedCapabilities method.

    java/src/org/openqa/selenium/grid/node/config/CapabilitiesMutatorService.java [43]

     public Capabilities getMutatedCapabilities(Capabilities desiredCapabilities) {
    +  Objects.requireNonNull(desiredCapabilities, "desiredCapabilities must not be null");
    +  ...
    +}
     

    ✨ Improve tool usage guide:

    Overview:
    The improve tool scans the PR code changes, and automatically generates suggestions for improving the PR code. The tool can be triggered automatically every time a new PR is opened, or can be invoked manually by commenting on a PR.
    When commenting, to edit configurations related to the improve tool (pr_code_suggestions section), use the following template:

    /improve --pr_code_suggestions.some_config1=... --pr_code_suggestions.some_config2=...
    

    With a configuration file, use the following template:

    [pr_code_suggestions]
    some_config1=...
    some_config2=...
    
    Enabling\disabling automation

    When you first install the app, the default mode for the improve tool is:

    pr_commands = ["/improve --pr_code_suggestions.summarize=true", ...]
    

    meaning the improve tool will run automatically on every PR, with summarization enabled. Delete this line to disable the tool from running automatically.

    Utilizing extra instructions

    Extra instructions are very important for the improve tool, since they enable to guide the model to suggestions that are more relevant to the specific needs of the project.

    Be specific, clear, and concise in the instructions. With extra instructions, you are the prompter. Specify relevant aspects that you want the model to focus on.

    Examples for extra instructions:

    [pr_code_suggestions] # /improve #
    extra_instructions="""
    Emphasize the following aspects:
    - Does the code logic cover relevant edge cases?
    - Is the code logic clear and easy to understand?
    - Is the code logic efficient?
    ...
    """
    

    Use triple quotes to write multi-line instructions. Use bullet points to make the instructions more readable.

    A note on code suggestions quality
    • While the current AI for code is getting better and better (GPT-4), it's not flawless. Not all the suggestions will be perfect, and a user should not accept all of them automatically.
    • Suggestions are not meant to be simplistic. Instead, they aim to give deep feedback and raise questions, ideas and thoughts to the user, who can then use his judgment, experience, and understanding of the code base.
    • Recommended to use the 'extra_instructions' field to guide the model to suggestions that are more relevant to the specific needs of the project, or use the custom suggestions 💎 tool
    • With large PRs, best quality will be obtained by using 'improve --extended' mode.
    More PR-Agent commands

    To invoke the PR-Agent, add a comment using one of the following commands:

    • /review: Request a review of your Pull Request.
    • /describe: Update the PR title and description based on the contents of the PR.
    • /improve [--extended]: Suggest code improvements. Extended mode provides a higher quality feedback.
    • /ask <QUESTION>: Ask a question about the PR.
    • /update_changelog: Update the changelog based on the PR's contents.
    • /add_docs 💎: Generate docstring for new components introduced in the PR.
    • /generate_labels 💎: Generate labels for the PR based on the PR's contents.
    • /analyze 💎: Automatically analyzes the PR, and presents changes walkthrough for each component.

    See the tools guide for more details.
    To list the possible configuration parameters, add a /config comment.

    See the improve usage page for a more comprehensive guide on using this tool.

    Custom capability mutators(with ordering priority) can be placed as part of Grid setup and will be used.
    
    Fixes SeleniumHQ#13628

    public class CapabilitiesMutatorService {
    Capabilities stereotype;
    private final List<CapabilityMutator> customMutators = new CopyOnWriteArrayList<>();
    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 the customMutators is expected to be populated as soon as the object gets created. After that Its only going to be iterated upon. So do we still need a CopyOnWriteArrayList? Can we not just use ArrayList instead ?

    private void loadAllCustomMutators() {
    ServiceLoader<CapabilityMutator> loader = ServiceLoader.load(CapabilityMutator.class);

    List<CapabilityMutator> allMutators = StreamSupport.stream(loader.spliterator(), false)
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    It would be good if you could create a common method that internally takes care of building a stream, sorting the collection and returning back the list. This common method should be called via the loadAllCustomMutators and setCustomMutators. That way, the sorting part is also tested out.

    import org.openqa.selenium.Platform;
    import org.openqa.selenium.SessionNotCreatedException;
    import org.openqa.selenium.WebDriverException;
    import org.openqa.selenium.*;
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    Can we please revert back this wildcard import?

    }

    public Capabilities getMutatedCapabilities(Capabilities desiredCapabilities) {
    Objects.requireNonNull(desiredCapabilities, "desiredCapabilities must not be null");
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    Selenium is using Require.nonNull. So for consistency sake, maybe we can use that instead of Objects.requireNonNull

    Objects.requireNonNull(desiredCapabilities, "desiredCapabilities must not be null");

    // Always apply this default capability mutator before applying any other mutator
    SessionCapabilitiesMutator defaultMutator = new SessionCapabilitiesMutator(stereotype);
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    The defaultMutator seems to be based off of stereoType which is the same throughout the lifecycle of this object. So does it need to be created multiple times?

    import org.openqa.selenium.remote.http.ClientConfig;
    import org.openqa.selenium.remote.http.HttpClient;
    import org.openqa.selenium.remote.service.DriverFinder;
    import org.openqa.selenium.remote.service.DriverService;
    import org.openqa.selenium.remote.tracing.AttributeKey;
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    Please help revert the wild card imports.

    @codecov-commenter
    Copy link

    Codecov Report

    All modified and coverable lines are covered by tests ✅

    Project coverage is 58.48%. Comparing base (7384157) to head (78bd7b2).

    ❗ Current head 78bd7b2 differs from pull request most recent head c6ea843. Consider uploading reports for the commit c6ea843 to get more accurate results

    ❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

    Additional details and impacted files
    @@           Coverage Diff           @@
    ##            trunk   #13672   +/-   ##
    =======================================
      Coverage   58.48%   58.48%           
    =======================================
      Files          86       86           
      Lines        5270     5270           
      Branches      220      220           
    =======================================
      Hits         3082     3082           
      Misses       1968     1968           
      Partials      220      220           

    ☔ View full report in Codecov by Sentry.
    📢 Have feedback on the report? Share it here.

    Copy link
    Member

    @diemol diemol left a comment

    Choose a reason for hiding this comment

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

    Thank you for starting this work, @Purus.

    I need help to grasp the general idea of the implementation. Why do we need a service for this? I do not understand the need to have several capability mutators. This implementation seems too complex for the requirement. I believe the idea is to overwrite the default capabilities mutator.

    Why not follow the concept we have with other pieces? For example, SlotMatcher and DefaultSlotMatcher?

    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.

    [🚀 Feature]: Supporting custom capabilities mutators for Grid
    5 participants