Skip to content

system/nxinit: add cmocka unit tests for parser/action/service - #3755

Draft
JianyuWang0623 wants to merge 2 commits into
apache:masterfrom
JianyuWang0623:nxinit-unit-tests-upstream
Draft

system/nxinit: add cmocka unit tests for parser/action/service#3755
JianyuWang0623 wants to merge 2 commits into
apache:masterfrom
JianyuWang0623:nxinit-unit-tests-upstream

Conversation

@JianyuWang0623

Copy link
Copy Markdown
Contributor

Summary

  • Fix a real bug in init_parse_config_lines(): a dead early continue for a truly-empty line (buf == "\0") skipped the memmove() bookkeeping its sibling whitespace-only-line branch performs, corrupting the remaining-length tracking and silently dropping every subsequent line in that refill chunk. The whitespace-skip loop right below already handles the empty-string case correctly, so the buggy early exit is simply redundant and removed.

  • Add a test/ subdirectory (mirroring apps/system/uorb/test/) with cmocka-based unit tests covering the logic most prone to regression in NxInit:

    • init_parse_arguments(): plain/quoted arguments, -- separator vs. --option long options (regression coverage for a previously fixed bug), argv-capacity truncation.
    • init_parse_config_file()/init_parse_config_lines(): section routing, blank/whitespace-only line skipping, unknown-section rejection, over-length line rejection, and a line straddling two read-buffer refills.
    • Action event matching (init_action_parse()/init_action_trigger_event()): exact match, invert (!=), fnmatch wildcards, AND semantics across multiple events per action (needs CONFIG_SYSTEM_NXINIT_ACTION_EVENTS_MAX > 1, skipped rather than failed at the default value of 1).
    • Service conflict detection (init_service_parse()/init_service_check()): duplicate service name rejection, override replacing an earlier duplicate, SERVICE_ARGS_MAX boundary.

    Tests exercise only the existing public parser.h/action.h/service.h API; no production symbols were made non-static. Test sources compile action.c/parser.c/service.c a second time into a separate nxinit_unit_test program, gated behind new CONFIG_SYSTEM_NXINIT_TEST (depends on TESTING_CMOCKA); the default init program is unaffected.

Opened as draft for early review/CI feedback.

Impact

  • Users: NO (test-only addition; the parser fix only removes dead/buggy code, no behavior change for well-formed configs).
  • Build: adds one new optional Kconfig-gated test target; default build unaffected.
  • Hardware: NO.
  • Documentation: NO.
  • Security: NO.
  • Compatibility: NO.

Testing

Build Host: Ubuntu 22.04 LTS x86_64, gcc 13.4.0 (Ubuntu 13.4.0-6ubuntu122ppa2)
Target: sim:citest (nuttx sim config, no cross toolchain needed; NxInit is pure apps-layer logic)

nxstyle/checkpatch.sh pass on all changed files.

Actual nxinit_unit_test runtime output on sim (default Kconfig, ACTION_EVENTS_MAX=1):

nsh> nxinit_unit_test
[==========] nxinit_tests: Running 17 test(s).
[ RUN      ] test_nxinit_parser_arguments_spaces
[       OK ] test_nxinit_parser_arguments_spaces
[ RUN      ] test_nxinit_parser_arguments_quoted
[       OK ] test_nxinit_parser_arguments_quoted
[ RUN      ] test_nxinit_parser_arguments_dashdash_separator
[       OK ] test_nxinit_parser_arguments_dashdash_separator
[ RUN      ] test_nxinit_parser_arguments_long_option
[       OK ] test_nxinit_parser_arguments_long_option
[ RUN      ] test_nxinit_parser_arguments_truncate
[       OK ] test_nxinit_parser_arguments_truncate
[ RUN      ] test_nxinit_parser_config_sections
[       OK ] test_nxinit_parser_config_sections
[ RUN      ] test_nxinit_parser_config_skip_blank_lines
[       OK ] test_nxinit_parser_config_skip_blank_lines
[ RUN      ] test_nxinit_parser_config_unknown_section
[       OK ] test_nxinit_parser_config_unknown_section
[ RUN      ] test_nxinit_parser_config_line_too_long
[       OK ] test_nxinit_parser_config_line_too_long
[ RUN      ] test_nxinit_parser_config_line_crosses_boundary
[       OK ] test_nxinit_parser_config_line_crosses_boundary
[ RUN      ] test_nxinit_action_event_match_exact
[       OK ] test_nxinit_action_event_match_exact
[ RUN      ] test_nxinit_action_event_match_invert
[       OK ] test_nxinit_action_event_match_invert
[ RUN      ] test_nxinit_action_event_match_fnmatch
[       OK ] test_nxinit_action_event_match_fnmatch
[ RUN      ] test_nxinit_action_event_and_semantics
[  SKIPPED ] test_nxinit_action_event_and_semantics
[ RUN      ] test_nxinit_service_duplicate_conflict
[       OK ] test_nxinit_service_duplicate_conflict
[ RUN      ] test_nxinit_service_override_replaces_duplicate
[       OK ] test_nxinit_service_override_replaces_duplicate
[ RUN      ] test_nxinit_service_args_max_boundary
[       OK ] test_nxinit_service_args_max_boundary
[==========] nxinit_tests: 17 test(s) run.
[  PASSED  ] 16 test(s).
[  SKIPPED ] nxinit_tests: 1 test(s), listed below:
[  SKIPPED ] test_nxinit_action_event_and_semantics

With CONFIG_SYSTEM_NXINIT_ACTION_EVENTS_MAX=4, test_nxinit_action_event_and_semantics runs and passes (17/17 PASSED), confirming the AND-semantics test is not dead code.

Both commits were re-verified by git cherry-pick-ing directly onto current apache/master (zero conflicts) before opening this PR, since they were originally authored on a branch that has since been squash-merged upstream via #3750.

init_parse_config_lines() had a dead early "continue" for a truly
empty line (buf == "\0") that skipped the memmove() bookkeeping its
sibling whitespace-only-line branch performs. When a real empty line
appeared mid-buffer, subsequent bytes were never shifted to the front
of the working buffer, corrupting the remaining-length tracking and
silently dropping every line after it for that refill chunk.

The whitespace-skip loop right below already handles the empty-string
case correctly (the loop body never executes, so it falls straight
into the "only whitespace" -> memmove -> continue path), so the buggy
early exit is simply redundant and removed.

Assisted-by: GitHubCopilot:claude-sonnet-5
Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
Add a test/ subdirectory (mirroring the apps/system/uorb/test/
layout) with cmocka-based unit tests covering the logic most prone
to regression in NxInit:

- init_parse_arguments(): plain/quoted arguments, the "--" separator
  vs. "--option" long options (regression coverage for a previously
  fixed bug), and argv-capacity truncation.
- init_parse_config_file()/init_parse_config_lines(): section
  routing, blank/whitespace-only line skipping, unknown-section
  rejection, over-length line rejection, and a line that straddles
  two read-buffer refills.
- Action event matching (init_action_parse()/
  init_action_trigger_event()): exact match, invert ("!="), fnmatch
  wildcards, and AND semantics across multiple events per action
  (the latter needs CONFIG_SYSTEM_NXINIT_ACTION_EVENTS_MAX > 1 and
  is skipped, not failed, at the default value of 1).
- Service conflict detection (init_service_parse()/
  init_service_check()): duplicate service name rejection, the
  "override" option replacing an earlier duplicate instead of
  failing, and the SERVICE_ARGS_MAX boundary.

Tests exercise only the existing public parser.h/action.h/service.h
API; no production symbols were made non-static. The test sources
compile action.c/parser.c/service.c a second time into a separate
"nxinit_unit_test" program, gated behind the new
CONFIG_SYSTEM_NXINIT_TEST (depends on TESTING_CMOCKA); the default
"init" program is unaffected.

While building this suite, a real bug was found in
init_parse_config_lines() and fixed in a separate commit.

Assisted-by: GitHubCopilot:claude-sonnet-5
Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
@JianyuWang0623
JianyuWang0623 force-pushed the nxinit-unit-tests-upstream branch 2 times, most recently from 3352764 to 1aacc30 Compare August 25, 2026 14:40
@cederom
cederom requested a review from linguini1 August 25, 2026 18:17
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.

1 participant