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

Simplify bootstrap function arguments with default-able options #2078

Merged
merged 6 commits into from
Apr 22, 2022

Conversation

soareschen
Copy link
Contributor

Crate: ibc-test-framwork

Description

Simplifies the interface for bootstrapping chain/client/connection/channel in integration tests. The optional arguments are now grouped into an option type like BootstrapChannelOptions with a Default instance. So callers who only want to provide the required arguments can pass in Default::default() for the optional arguments.

This will also allow the bootstrap functions to evolve more easily in the future as more overrides are added, which can be added to the options types.


PR author checklist:

  • Added changelog entry, using unclog.
  • Added tests: integration (for Hermes) or unit/mock tests (for modules).
  • Linked to GitHub issue.
  • Updated code comments and documentation (e.g., docs/).

Reviewer checklist:

  • Reviewed Files changed in the GitHub PR explorer.
  • Manually tested (in case integration/unit/mock tests are absent).

Copy link
Contributor

@mzabaluev mzabaluev left a comment

Choose a reason for hiding this comment

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

The argument structs are good, but the other changes look like a revert to an earlier system with 5-argument functions.

Comment on lines -39 to -54
impl<'config> Builder<'config> {
/// Initializes the builder with two [`FullNode`] values representing two different
/// running full nodes, to bootstrap chain A and chain B respectively.
pub fn with_node_pair(
test_config: &'config TestConfig,
node_a: FullNode,
node_b: FullNode,
) -> Self {
Self {
test_config,
node_a,
node_b,
client_options_a_to_b: Default::default(),
client_options_b_to_a: Default::default(),
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why remove the builder for a less convenient function API? This has the same purpose: make the test code more convenient to write in the simplest cases, while making customization possible.

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 think the builder pattern is fine as long as it does not force the user to always use the pattern. There is a trade off between readability and writability. When I tried to use the new builder pattern, I found it much more difficult to understand, as I now have to jump between dozens of lines of code with mutation, side effects, and could potentially be executed non-linearly. In comparison, function argument passing have linear semantics, so I just need to read the few lines to immediately understand the semantics of the code.

Anyway, I re-introduce the builder pattern in aa336c7 with the pattern applied to the BootstrapClientOptions type instead. The builder methods are opt-in, with the Default trait or explicit field accessors available in case users want to opt out. This helps make it clear that there are only syntactic sugar methods and users only need to worry about side effects in the other functions.

Copy link
Contributor

@mzabaluev mzabaluev Apr 21, 2022

Choose a reason for hiding this comment

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

The mandatory config_modifier closure argument is also annoying. In the builder, I tried to simplify this by having two finalization methods: bootstrap as the basic case without the modifier closure, and bootstrap_with_config to provide the modifier. Passing the closure to the final build method also hints the user that the closure is invoked during the bootstrap process, not as part of the option setup.

Readability is subjective here. To me, it's much more intuitive to read the builder method chain where most of the parameters are supplied to a clearly named method, if they need to be customized at all, rather than trying to remember the meaning or the "dunno, use default" value of each of the anonymous arguments in a function call site. This is the rationale behind the clippy lint and the related API guideline. In this case it's not that bad, three to five parameters with distinct types, so let's go with this. These utilities are used by the test framework, not the tests themselves, so readability and convenience is less important here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah the main point is that these are internal functions that are not meant to be called frequently. They act more as code fragments not as reusable modules. The higher level constructs like BinaryChainTest are the real abstraction that hides the complexity, so we only need to focus on providing clean interfaces there.

@soareschen soareschen merged commit dffe1cb into master Apr 22, 2022
@soareschen soareschen deleted the soares/bootstrap-options branch April 22, 2022 10:48
hu55a1n1 pushed a commit to hu55a1n1/hermes that referenced this pull request Sep 13, 2022
…rmalsystems#2078)

* Refactor bootstrap chain and foreign client code

* Introduce BootstrapChannel/ConnectionOptions

* Use builder pattern for bootstrap option types
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants