You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat(parity): add the api parity file and its test - #411
This PR adds api-parity.toml, a table of builder options per entity and per binding (Rust, Python, C and Go). It also adds a CI test that fails when a public with_* builder method has no row. The scan reads only impl headers that start a line and the macro impl_with_type_info!. It demands rows only for Rust with_* methods, so an option that exists only in one binding needs no row. The table already holds the rows for the options that the other 0.2.2 pull requests add.
Merge this PR after those pull requests, because the test fails on a builder method that has no row. This PR starts from the v0.2.1 tag and carries the rows, so it passes only on a tree that holds the other changes.
Key Changes
Area
Change
api-parity.toml
One row per builder option, for each entity. Each cell is present with a symbol, absent with a reason, or planned with a reason and a release.
crates/hiroz-tests/tests/api_parity.rs
Parser test, builder scan, symbol check and control tests. A present Python, C or Go symbol must be defined in the source of its binding, outside comments and strings. For Python and C, a definition is a Rust fn, struct, enum, union, trait, type, const or static item. A parameter of a fn, a field of a struct, a PyO3 signature parameter and a PyO3 rename attribute also count. For Go, it is a func or type line. The check reads nested Rust block comments and raw strings. A call, a struct literal, a let binding, a closure parameter and a format argument do not count. It runs in the pure-Rust CI job. The scan reads pub fn, pub async fn, pub const fn and pub unsafe fn.
crates/hiroz-tests/Cargo.toml
The toml crate as a dependency of the test crate.
CONTRIBUTING.md
A short note that a new builder method needs a row, and what the scan and the symbol check cover.
api-parity.toml header
States what the symbol check proves. Each identifier of a present Python, C or Go symbol is defined in the source of that binding, outside comments and strings. The header no longer says that the symbol exists as a whole.
Rows for the options of the other 0.2.2 pull requests: the node subscriber options, the publisher locality, the cache locality and the cache expected encoding. The cells for the C and Go buffer group, the C quality-of-service v2 structs and the C action client status topic are present. The Go context namespace is planned for 0.3.0.
The file has no entity for parameters or lifecycle nodes. It has no rows for events. It does not compare behaviour between bindings. The symbol check does not tie a method to its type or a parameter to its function: a parameter or field name counts when any function or struct of the binding has it. It does not check enum variants, impl blocks, macro-generated items, Go struct fields or Go parameters, and it does not run the binding.
Before and after
Before
After
Parity file
none
api-parity.toml
New with_* method on a builder
needs no row
fails the test until it has a row
Row that names a missing symbol
not possible
fails the test
Scan that finds no builder method
not possible
fails the test
present cell for Python, C or Go
not possible
fails the test when an identifier of the symbol has no definition in the source of that binding. A mention in a comment, a nested comment, a string or a raw string, a commented-out rename attribute, a call, a struct literal or a let binding does not count
Builder method declared pub async fn or pub const fn
not scanned
scanned
Test
Before
After
parity_file_has_valid_shape
fails: the file does not exist
passes
every_with_method_has_a_row
does not exist
passes on a tree that holds the other 0.2.2 changes
a_new_with_method_fails_until_it_has_a_row
does not exist
passes: a new method without a row fails
deleting_a_row_fails_the_check
does not exist
passes: it removes one row and expects a failure
scan_reads_impl_headers_and_macro_calls
does not exist
passes. With the async and const scan change reverted, it fails
a_symbol_that_is_only_mentioned_fails
does not exist
passes: a symbol that occurs only in a comment, a string or a call is reported for each of the three bindings, and the same symbol passes once it is defined
definition_in_a_commented_rename_fails
does not exist: the file is absent on main
passes: a rename in a comment or a string is not a definition, and a live rename is
Nothing fails on the tag today. A builder option can ship in Rust with no Python, C or Go option, and no file records the gap. A later pull request that adds a with_* method passes CI with no row. With this PR, it fails with a message that names the method.
The first commit is red because the file does not exist. On a tree without the rows of the other pull requests, every_with_method_has_a_row, deleting_a_row_fails_the_check and a_new_with_method_fails_until_it_has_a_row fail and name the four new methods. That failure is the reason this PR lands last.
Evidence
The parity tests ran on a scratch merge. It joins the head of this branch and the heads of the other sixteen 0.2.2 changes: fix/embedded-msgs-crates-io, chore/yanked-crates, chore/uv-lock-check, fix/go-sequence-length-bound, fix/go-time-array, fix/go-nested-fixed-array, fix/python-send-response-id, docs/python-binding-guide, feat/node-group-options, feat/c-go-group-choice, feat/cache-check-parity, feat/c-abi-version-guard, feat/raw-subscriber-buffer-fields, feat/c-qos-v2, fix/go-typed-subscriber-nil-receiver and feat/go-context-variants. The merge commit is not published. Two conflicts needed a hand edit. In crates/hiroz/src/ffi/service.rs, the merge takes the _with_qos call from the QoS change and the ffi_warn! macro from the version change. In crates/hiroz-go/hiroz/subscriber_typed.go, it keeps both import lists. The merge does not hold the CI wiring change or the release change.
Claim
Basis
The parity tests pass on a tree with the other 0.2.2 changes
cargo nextest run --no-tests=fail -p hiroz-tests --features ros-msgs,jazzy,raw-sub-tests -E 'binary(api_parity)' runs 47 tests and all 47 pass (rc 0) on that merge, with this branch at 58b539019
The stricter symbol check accepts every real present cell
present_symbols_exist_in_each_binding is one of those tests. It passes with every present cell of api-parity.toml unchanged
The stricter symbol check rejects the gaps
The tests in the table above, one per gap, are among those tests. The red results in the table above were measured at earlier heads and not re-run at 58b539019. The tests ffe66d6ec adds and the multibyte test a9409ff7e adds were each red before their fix
Lint
cargo clippy -p hiroz-tests --no-deps --all-targets --features ros-interop,hu-meter-tests,hu-monitor-tests,raw-sub-tests,jazzy -- -D warnings ends with rc 0 on the same merge
The builder scan ignores comments and strings, and a named = argument in a call inside a PyO3 default is not a definition
builder_scan_ignores_comments_and_strings, builder_scan_keeps_methods_after_a_raw_string_with_a_closing_brace and a_format_argument_in_a_python_default_is_not_a_definition pass in the same run. Not run against the old scan: no red result was measured for them
The scanner gaps are closed
a_format_argument_in_a_brace_delimited_macro_is_not_a_definition, builder_scan_reads_an_impl_header_that_spans_lines and a_symbol_without_an_identifier_fails, a_string_default_of_a_parameter_called_name_is_not_a_rename and a_pyo3_signature_parameter_defines_a_python_name_only and a_forbidden_field_is_rejected_whatever_its_value pass on 58b539019. Each fails when its own fix is reverted alone: brace depth, the joined impl header, the empty-identifier check, the depth of a rename, the Python-only signature rule, the key check of a forbidden cell field
The scan fix is covered
not re-measured at 58b539019. An earlier run with the async and const scan change reverted failed only scan_reads_impl_headers_and_macro_calls
They fail without the rows of the other pull requests
not re-measured at 58b539019. An earlier run on a tree that lacked the rows failed three tests, every_with_method_has_a_row, deleting_a_row_fails_the_check and a_new_with_method_fails_until_it_has_a_row
The test scans the builder types in crates/hiroz/src and fails when a public with_* method has no parity row, or when a row names a symbol that does not exist. Control tests prove that an empty scan, a deleted row and a new method each fail.
The header said every symbol exists in the source of its binding. The check requires each identifier to be defined there, outside comments and strings. State that, in the same words as the test and CONTRIBUTING.md.
The change that adds the node default now carries the corrected sentence. Keeping the edit here conflicts with the neighbouring bullets of that change.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
The parity checks can miss API mismatches because they scan commented-out methods, skip multiline impl headers, and accept malformed symbols or status fields.
Validate supplied fields before status-specific checks
crates/hiroz-tests/tests/api_parity.rs:96
non_empty_str treats an invalid field as though it were absent, which lets malformed cells pass the shape check. For example, a valid present cell with reason = 123 or release = "" is accepted even though those keys are forbidden for that status; the same bypass applies to the forbidden fields of absent and planned cells. Validate any supplied field as a nonempty string before applying the status-specific checks.
Parse complete multiline impl headers
crates/hiroz-tests/tests/api_parity.rs:469
A column-zero impl with a multiline generic header is silently skipped because impl_self_type receives only its first line and cannot find the closing >. When other blocks supply the builder's existing methods, the positive controls still pass, so new with_* methods in the skipped block need no parity rows. Accumulate the complete header before extracting its self type, and add a multiline-header regression case.
Reject symbols that produce no identifiers
crates/hiroz-tests/tests/api_parity.rs:940
A nonempty symbol containing only punctuation, such as "?" or "...", passes shape validation but produces no identifiers here. The loop then performs no existence checks, so a present Python, C or Go cell with no symbol definition still passes CI. Reject symbols that produce an empty identifier list before checking their definitions.
The builder scan now blanks comments and strings before it reads impl blocks. The pyo3 signature scan reads only the top level of the parameter list, so a named format argument in a default is not a definition.
Braces now count toward the PyO3 signature depth, so a brace-delimited format argument defines nothing. An impl header that spans lines is read up to its opening brace. A present cell whose symbol has no identifier is an error.
A parameter called name in a PyO3 signature is not a rename, so its string default defines nothing. The signature parameter rule applies to the Python binding only, as the module comment says.
A forbidden field was checked as a non-empty string, so symbol = 123, release = [] and reason = "" passed. Check that the key is present.
This branch has not been deployed
No deployments
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds
api-parity.toml, a table of builder options per entity and per binding (Rust, Python, C and Go). It also adds a CI test that fails when a publicwith_*builder method has no row. The scan reads onlyimplheaders that start a line and the macroimpl_with_type_info!. It demands rows only for Rustwith_*methods, so an option that exists only in one binding needs no row. The table already holds the rows for the options that the other 0.2.2 pull requests add.Merge this PR after those pull requests, because the test fails on a builder method that has no row. This PR starts from the v0.2.1 tag and carries the rows, so it passes only on a tree that holds the other changes.
Key Changes
api-parity.tomlpresentwith a symbol,absentwith a reason, orplannedwith a reason and a release.crates/hiroz-tests/tests/api_parity.rspresentPython, C or Go symbol must be defined in the source of its binding, outside comments and strings. For Python and C, a definition is a Rustfn,struct,enum,union,trait,type,constorstaticitem. A parameter of afn, a field of astruct, a PyO3signatureparameter and a PyO3 rename attribute also count. For Go, it is afuncortypeline. The check reads nested Rust block comments and raw strings. A call, a struct literal, aletbinding, a closure parameter and a format argument do not count. It runs in the pure-Rust CI job. The scan readspub fn,pub async fn,pub const fnandpub unsafe fn.crates/hiroz-tests/Cargo.tomltomlcrate as a dependency of the test crate.CONTRIBUTING.mdapi-parity.tomlheaderpresentPython, C or Go symbol is defined in the source of that binding, outside comments and strings. The header no longer says that the symbol exists as a whole.Rows for the options of the other 0.2.2 pull requests: the node subscriber options, the publisher locality, the cache locality and the cache expected encoding. The cells for the C and Go buffer group, the C quality-of-service v2 structs and the C action client status topic are
present. The Go context namespace isplannedfor 0.3.0.The file has no entity for parameters or lifecycle nodes. It has no rows for events. It does not compare behaviour between bindings. The symbol check does not tie a method to its type or a parameter to its function: a parameter or field name counts when any function or struct of the binding has it. It does not check enum variants,
implblocks, macro-generated items, Go struct fields or Go parameters, and it does not run the binding.Before and after
api-parity.tomlwith_*method on a builderpresentcell for Python, C or Goletbinding does not countpub async fnorpub const fnparity_file_has_valid_shapeevery_with_method_has_a_rowa_new_with_method_fails_until_it_has_a_rowdeleting_a_row_fails_the_checkscan_reads_impl_headers_and_macro_callsa_symbol_that_is_only_mentioned_failsdefinition_in_a_commented_rename_failsmaindefinition_in_a_nested_rust_comment_failsdefinition_after_a_rust_raw_string_is_read_correctlya_name_that_is_not_a_parameter_or_field_failsmultibyte_text_before_a_rename_keeps_offsets_stableWhat fails without this
Nothing fails on the tag today. A builder option can ship in Rust with no Python, C or Go option, and no file records the gap. A later pull request that adds a
with_*method passes CI with no row. With this PR, it fails with a message that names the method.The first commit is red because the file does not exist. On a tree without the rows of the other pull requests,
every_with_method_has_a_row,deleting_a_row_fails_the_checkanda_new_with_method_fails_until_it_has_a_rowfail and name the four new methods. That failure is the reason this PR lands last.Evidence
The parity tests ran on a scratch merge. It joins the head of this branch and the heads of the other sixteen 0.2.2 changes:
fix/embedded-msgs-crates-io,chore/yanked-crates,chore/uv-lock-check,fix/go-sequence-length-bound,fix/go-time-array,fix/go-nested-fixed-array,fix/python-send-response-id,docs/python-binding-guide,feat/node-group-options,feat/c-go-group-choice,feat/cache-check-parity,feat/c-abi-version-guard,feat/raw-subscriber-buffer-fields,feat/c-qos-v2,fix/go-typed-subscriber-nil-receiverandfeat/go-context-variants. The merge commit is not published. Two conflicts needed a hand edit. Incrates/hiroz/src/ffi/service.rs, the merge takes the_with_qoscall from the QoS change and theffi_warn!macro from the version change. Incrates/hiroz-go/hiroz/subscriber_typed.go, it keeps both import lists. The merge does not hold the CI wiring change or the release change.cargo nextest run --no-tests=fail -p hiroz-tests --features ros-msgs,jazzy,raw-sub-tests -E 'binary(api_parity)'runs 47 tests and all 47 pass (rc 0) on that merge, with this branch at58b539019presentcellpresent_symbols_exist_in_each_bindingis one of those tests. It passes with everypresentcell ofapi-parity.tomlunchanged58b539019. The testsffe66d6ecadds and the multibyte testa9409ff7eadds were each red before their fixcargo clippy -p hiroz-tests --no-deps --all-targets --features ros-interop,hu-meter-tests,hu-monitor-tests,raw-sub-tests,jazzy -- -D warningsends with rc 0 on the same mergenamed =argument in a call inside a PyO3 default is not a definitionbuilder_scan_ignores_comments_and_strings,builder_scan_keeps_methods_after_a_raw_string_with_a_closing_braceanda_format_argument_in_a_python_default_is_not_a_definitionpass in the same run. Not run against the old scan: no red result was measured for thema_format_argument_in_a_brace_delimited_macro_is_not_a_definition,builder_scan_reads_an_impl_header_that_spans_linesanda_symbol_without_an_identifier_fails,a_string_default_of_a_parameter_called_name_is_not_a_renameanda_pyo3_signature_parameter_defines_a_python_name_onlyanda_forbidden_field_is_rejected_whatever_its_valuepass on58b539019. Each fails when its own fix is reverted alone: brace depth, the joinedimplheader, the empty-identifier check, the depth of a rename, the Python-only signature rule, the key check of a forbidden cell field58b539019. An earlier run with the async and const scan change reverted failed onlyscan_reads_impl_headers_and_macro_calls58b539019. An earlier run on a tree that lacked the rows failed three tests,every_with_method_has_a_row,deleting_a_row_fails_the_checkanda_new_with_method_fails_until_it_has_a_rowBreaking Changes
None.