Skip to content

Commit 806b2ff

Browse files
manoecursoragent
andcommitted
printer sid BUGFIX include nodes augmented into other modules
The SID generator collected data-namespace items only from the processed module's own compiled tree (lysc_module_dfs_full(module, ...)). Nodes that the module contributes to other modules by augmentation live in the target modules' trees, so they were never collected and got no SID. For models that place feature data via augments (e.g. augmenting a common root module), the generated .sid file ended up with just the module item and no data items. Collect all nodes defined by the processed module wherever they are grafted: after walking the module's own tree, also walk every context module whose augmented_by references the processed module. collect_data_cb now attributes each node to its defining module (node->module), so a foreign module's own nodes are skipped while traversing its tree, and a module's .sid no longer wrongly includes nodes augmented into it by others. Add test_augment covering both directions (the augmenting module's .sid contains the augmented-in node; the base module's .sid does not). Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 7142ccf commit 806b2ff

2 files changed

Lines changed: 111 additions & 0 deletions

File tree

src/printer_sid.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ struct sid_collect_data {
5050
uint64_t count; /**< Number of items collected so far; exceeding @p max, surplus items are only counted. */
5151
uint64_t max; /**< Maximum number of items (capacity of the array). */
5252
struct ly_ctx *ctx; /**< libyang context used for error logging. */
53+
const struct lys_module *module; /**< Module whose nodes are collected; used to attribute augmented nodes to their defining module. */
5354
};
5455

5556
/**
@@ -178,6 +179,13 @@ collect_data_cb(struct lysc_node *node, void *data, ly_bool *UNUSED(dfs_continue
178179
return LY_SUCCESS; /* choice/case get no item, but their subtree is still traversed */
179180
}
180181

182+
/* collect only nodes defined by the module being processed; when another
183+
* module's tree is traversed this keeps exactly the nodes augmented in by
184+
* the processed module and skips the target module's own nodes */
185+
if (node->module != collect_data->module) {
186+
return LY_SUCCESS;
187+
}
188+
181189
path = sid_node_path(node);
182190
LY_CHECK_ERR_RET(!path, LOGMEM(collect_data->ctx), LY_EMEM);
183191

@@ -343,12 +351,42 @@ sid_collect_items(struct sid_collect_data *callback_data, const struct lys_modul
343351
LY_CHECK_RET((rc = sid_item_add(callback_data, "feature", feature->name)), rc);
344352
}
345353

354+
/* collect_data_cb attributes nodes to their defining module, so foreign
355+
nodes are skipped whenever another module's tree is traversed below */
356+
callback_data->module = module;
357+
346358
/* data namespace: walk the entire compiled schema tree depth-first.
347359
lysc_module_dfs_full traverses all nodes including RPCs, actions,
348360
notifications, input, output, choice and case nodes. sid_node_path() builds the
349361
RFC 9595 schema-node-path identifiers (choice/case names omitted). */
350362
LY_CHECK_RET((rc = lysc_module_dfs_full(module, collect_data_cb, callback_data)), rc);
351363

364+
/* data namespace: nodes that this module augments into other modules live in
365+
the target modules' trees (RFC 9595 still assigns them to this module's .sid).
366+
Traverse every context module that lists this module in its augmented_by and
367+
collect the nodes defined here (collect_data_cb filters by defining module). */
368+
{
369+
const struct lys_module *aug_target;
370+
uint32_t mod_idx = 0;
371+
372+
while ((aug_target = ly_ctx_get_module_iter(module->ctx, &mod_idx))) {
373+
ly_bool augmented = 0;
374+
375+
if ((aug_target == module) || !aug_target->compiled) {
376+
continue;
377+
}
378+
LY_ARRAY_FOR(aug_target->augmented_by, i) {
379+
if (aug_target->augmented_by[i] == module) {
380+
augmented = 1;
381+
break;
382+
}
383+
}
384+
if (augmented) {
385+
LY_CHECK_RET((rc = lysc_module_dfs_full(aug_target, collect_data_cb, callback_data)), rc);
386+
}
387+
}
388+
}
389+
352390
/* data namespace: also traverse the data trees of compiled top-level extension
353391
instances that define their own data tree outside the standard module trees
354392
(rc:yang-data, sx:structure); the top-level data node is obtained from the

tests/utests/schema/test_printer_sid.c

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,78 @@ test_exts(void **state)
453453
lyd_free_all(sid_file);
454454
}
455455

456+
/**
457+
* @brief Test case: augment coverage. A module's .sid file must include the data
458+
* nodes it augments into another module (RFC 9595), and a module's own .sid must
459+
* not include nodes augmented into it by other modules.
460+
*/
461+
static void
462+
test_augment(void **state)
463+
{
464+
struct lys_module *base, *aug;
465+
struct lyd_node *sid_file = NULL;
466+
static const char *mod_base =
467+
"module b1 {\n"
468+
" yang-version 1.1;\n"
469+
" namespace \"urn:b1\";\n"
470+
" prefix b1;\n"
471+
" revision 2024-01-01;\n"
472+
" container cont {\n"
473+
" leaf l { type string; }\n"
474+
" }\n"
475+
"}\n";
476+
static const char *mod_aug =
477+
"module a1 {\n"
478+
" yang-version 1.1;\n"
479+
" namespace \"urn:a1\";\n"
480+
" prefix a1;\n"
481+
" import b1 { prefix b1; }\n"
482+
" revision 2024-01-01;\n"
483+
" augment \"/b1:cont\" {\n"
484+
" leaf x { type string; }\n"
485+
" }\n"
486+
"}\n";
487+
488+
UTEST_ADD_MODULE(mod_base, LYS_IN_YANG, NULL, &base);
489+
UTEST_ADD_MODULE(mod_aug, LYS_IN_YANG, NULL, &aug);
490+
assert_non_null(ly_ctx_load_module(_UC->ctx, "ietf-sid-file", NULL, NULL));
491+
492+
/* the augmenting module's .sid contains the node it augments in: /b1:cont/a1:x */
493+
assert_int_equal(LY_SUCCESS, lys_sid_gen(aug, 100, 2, LYS_SID_FILE_UNPUBLISHED, "d", &sid_file));
494+
assert_non_null(sid_file);
495+
check_json_tree(sid_file,
496+
"{\"ietf-sid-file:sid-file\":{"
497+
"\"module-name\":\"a1\","
498+
"\"module-revision\":\"2024-01-01\","
499+
"\"sid-file-status\":\"unpublished\","
500+
"\"description\":\"d\","
501+
"\"dependency-revision\":[{\"module-name\":\"b1\",\"module-revision\":\"2024-01-01\"}],"
502+
"\"assignment-range\":[{\"entry-point\":\"100\",\"size\":\"2\"}],"
503+
"\"item\":["
504+
"{\"namespace\":\"module\",\"identifier\":\"a1\",\"status\":\"unstable\",\"sid\":\"100\"},"
505+
"{\"namespace\":\"data\",\"identifier\":\"/b1:cont/a1:x\",\"status\":\"unstable\",\"sid\":\"101\"}"
506+
"]}}");
507+
lyd_free_all(sid_file);
508+
sid_file = NULL;
509+
510+
/* the base module's own .sid must not contain the node augmented in by a1 */
511+
assert_int_equal(LY_SUCCESS, lys_sid_gen(base, 100, 3, LYS_SID_FILE_UNPUBLISHED, "d", &sid_file));
512+
assert_non_null(sid_file);
513+
check_json_tree(sid_file,
514+
"{\"ietf-sid-file:sid-file\":{"
515+
"\"module-name\":\"b1\","
516+
"\"module-revision\":\"2024-01-01\","
517+
"\"sid-file-status\":\"unpublished\","
518+
"\"description\":\"d\","
519+
"\"assignment-range\":[{\"entry-point\":\"100\",\"size\":\"3\"}],"
520+
"\"item\":["
521+
"{\"namespace\":\"module\",\"identifier\":\"b1\",\"status\":\"unstable\",\"sid\":\"100\"},"
522+
"{\"namespace\":\"data\",\"identifier\":\"/b1:cont\",\"status\":\"unstable\",\"sid\":\"101\"},"
523+
"{\"namespace\":\"data\",\"identifier\":\"/b1:cont/l\",\"status\":\"unstable\",\"sid\":\"102\"}"
524+
"]}}");
525+
lyd_free_all(sid_file);
526+
}
527+
456528
int
457529
main(void)
458530
{
@@ -461,6 +533,7 @@ main(void)
461533
UTEST(test_gen),
462534
UTEST(test_flow),
463535
UTEST(test_exts),
536+
UTEST(test_augment),
464537
};
465538

466539
return cmocka_run_group_tests(tests, NULL, NULL);

0 commit comments

Comments
 (0)