schema BUGFIX processing extension instance argument
Due to differences in YANG and YIN, the extension instance's argument is
processed differently. So far, the argument was missing when the source
format was YIN and extension was not compiled. This was breaking
printers and limiting even other use cases of the argument.
This patch solves the problem by getting the extension definition when
needed and finishing the argument processing not finished by YIN parser.
diff --git a/tests/utests/CMakeLists.txt b/tests/utests/CMakeLists.txt
index 4e3543f..5b17b5c 100644
--- a/tests/utests/CMakeLists.txt
+++ b/tests/utests/CMakeLists.txt
@@ -24,7 +24,7 @@
ly_add_utest(NAME xpath SOURCES basic/test_xpath.c)
ly_add_utest(NAME yanglib SOURCES basic/test_yanglib.c)
-ly_add_utest(NAME schema SOURCES schema/test_schema.c schema/test_schema_common.c schema/test_schema_stmts.c)
+ly_add_utest(NAME schema SOURCES schema/test_schema.c schema/test_schema_common.c schema/test_schema_stmts.c schema/test_schema_extensions.c)
ly_add_utest(NAME parser_yang SOURCES schema/test_parser_yang.c)
ly_add_utest(NAME parser_yin SOURCES schema/test_parser_yin.c)
ly_add_utest(NAME tree_schema_compile SOURCES schema/test_tree_schema_compile.c)
diff --git a/tests/utests/extensions/test_metadata.c b/tests/utests/extensions/test_metadata.c
index ca5558c..163785b 100644
--- a/tests/utests/extensions/test_metadata.c
+++ b/tests/utests/extensions/test_metadata.c
@@ -125,7 +125,7 @@
"<md:annotation name=\"aa\"/>\n"
"</module>";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL));
- CHECK_LOG_CTX("Missing mandatory keyword \"type\" as a child of \"md:annotation\".", "/aa:{extension='md:annotation'}/aa");
+ CHECK_LOG_CTX("Missing mandatory keyword \"type\" as a child of \"md:annotation aa\".", "/aa:{extension='md:annotation'}/aa");
/* not allowed substatement */
data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n"
@@ -135,7 +135,7 @@
" <default value=\"x\"/>\n"
"</md:annotation></module>";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL));
- CHECK_LOG_CTX("Invalid keyword \"default\" as a child of \"md:annotation\" extension instance.", "/aa:{extension='md:annotation'}/aa");
+ CHECK_LOG_CTX("Invalid keyword \"default\" as a child of \"md:annotation aa\" extension instance.", "/aa:{extension='md:annotation'}/aa");
/* invalid cardinality of units substatement */
data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n"
diff --git a/tests/utests/extensions/test_yangdata.c b/tests/utests/extensions/test_yangdata.c
index 02b50f1..7bf1cc6 100644
--- a/tests/utests/extensions/test_yangdata.c
+++ b/tests/utests/extensions/test_yangdata.c
@@ -210,8 +210,7 @@
"import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
"rc:yang-data { container x { leaf x {type string;}}}}";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Extension plugin \"libyang 2 - yang-data, version 1\": "
- "Extension rc:yang-data is instantiated without mandatory argument representing YANG data template name.",
+ CHECK_LOG_CTX("Extension instance \"rc:yang-data\" misses argument element \"name\".",
"/a:{extension='rc:yang-data'}");
data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;"
diff --git a/tests/utests/schema/test_parser_yin.c b/tests/utests/schema/test_parser_yin.c
index 14f6b7d..9292f0d 100644
--- a/tests/utests/schema/test_parser_yin.c
+++ b/tests/utests/schema/test_parser_yin.c
@@ -121,7 +121,7 @@
#define ELEMENT_WRAPPER_END "</status>"
#define TEST_1_CHECK_LYSP_EXT_INSTANCE(NODE, INSUBSTMT)\
- CHECK_LYSP_EXT_INSTANCE((NODE), NULL, 1, NULL, INSUBSTMT, 0, "myext:c-define", LY_PREF_XML)
+ CHECK_LYSP_EXT_INSTANCE((NODE), NULL, 1, INSUBSTMT, 0, "myext:c-define", LY_PREF_XML)
struct lys_yin_parser_ctx *YCTX;
@@ -335,7 +335,7 @@
ret = yin_parse_extension_instance(YCTX, LY_STMT_CONTACT, 0, &exts);
assert_int_equal(ret, LY_SUCCESS);
- CHECK_LYSP_EXT_INSTANCE(exts, NULL, 1, NULL, LY_STMT_CONTACT, 0, "myext:ext", LY_PREF_XML);
+ CHECK_LYSP_EXT_INSTANCE(exts, NULL, 1, LY_STMT_CONTACT, 0, "myext:ext", LY_PREF_XML);
CHECK_LYSP_STMT(exts->child, arg, 0, LYS_YIN_ATTR, 0, 1, stmt);
stmt = "value";
@@ -355,7 +355,7 @@
ret = yin_parse_extension_instance(YCTX, LY_STMT_CONTACT, 0, &exts);
assert_int_equal(ret, LY_SUCCESS);
- CHECK_LYSP_EXT_INSTANCE(exts, NULL, 0, NULL, LY_STMT_CONTACT, 0, "myext:extension-elem", LY_PREF_XML);
+ CHECK_LYSP_EXT_INSTANCE(exts, NULL, 0, LY_STMT_CONTACT, 0, "myext:extension-elem", LY_PREF_XML);
lysp_ext_instance_free(UTEST_LYCTX, exts);
LY_ARRAY_FREE(exts);
exts = NULL;
@@ -377,7 +377,7 @@
ret = yin_parse_extension_instance(YCTX, LY_STMT_CONTACT, 0, &exts);
assert_int_equal(ret, LY_SUCCESS);
- CHECK_LYSP_EXT_INSTANCE(exts, NULL, 1, NULL, LY_STMT_CONTACT, 0, "myext:ext", LY_PREF_XML);
+ CHECK_LYSP_EXT_INSTANCE(exts, NULL, 1, LY_STMT_CONTACT, 0, "myext:ext", LY_PREF_XML);
stmt = "attr1";
arg = "text1";
@@ -544,7 +544,7 @@
const char *exts_name = "myext:custom";
const char *exts_arg = "totally amazing extension";
- CHECK_LYSP_EXT_INSTANCE(exts, exts_arg, 0, NULL, LY_STMT_PREFIX, 0, exts_name, LY_PREF_XML);
+ CHECK_LYSP_EXT_INSTANCE(exts, exts_arg, 0, LY_STMT_PREFIX, 0, exts_name, LY_PREF_XML);
assert_string_equal(value, "wsefsdf");
assert_string_equal(units, "radians");
assert_string_equal(when_p->cond, "condition...");
diff --git a/tests/utests/schema/test_schema.c b/tests/utests/schema/test_schema.c
index dd9e34c..3c49977 100644
--- a/tests/utests/schema/test_schema.c
+++ b/tests/utests/schema/test_schema.c
@@ -26,7 +26,11 @@
const char **module_data, void (**free_module_data)(void *model_data, void *user_data))
{
*module_data = user_data;
- *format = LYS_IN_YANG;
+ if ((*module_data)[0] == '<') {
+ *format = LYS_IN_YIN;
+ } else {
+ *format = LYS_IN_YANG;
+ }
*free_module_data = NULL;
return LY_SUCCESS;
}
@@ -46,6 +50,10 @@
void test_identity(void **state);
void test_feature(void **state);
+/* test_schema_extensions.c */
+void test_extension_argument(void **state);
+void test_extension_argument_element(void **state);
+
int
main(void)
{
@@ -61,6 +69,10 @@
/** test_schema_stmts.c */
UTEST(test_identity),
UTEST(test_feature),
+
+ /** test_schema_extensions.c */
+ UTEST(test_extension_argument),
+ UTEST(test_extension_argument_element),
};
return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tests/utests/schema/test_schema_extensions.c b/tests/utests/schema/test_schema_extensions.c
new file mode 100644
index 0000000..7c15773
--- /dev/null
+++ b/tests/utests/schema/test_schema_extensions.c
@@ -0,0 +1,288 @@
+/*
+ * @file test_schema_extensions.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for YANG (YIN) extension statements and their instances in schemas
+ *
+ * Copyright (c) 2018-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#include "test_schema.h"
+
+#include <string.h>
+
+#include "context.h"
+#include "log.h"
+#include "tree_schema.h"
+
+void
+test_extension_argument(void **state)
+{
+ const struct lys_module *mod;
+ const char *mod_def_yang = "module a {\n"
+ " namespace \"urn:a\";\n"
+ " prefix a;\n\n"
+ " extension e {\n"
+ " argument name;\n"
+ " }\n\n"
+ " a:e \"aaa\";\n"
+ "}\n";
+ const char *mod_def_yin =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"a\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:a=\"urn:a\">\n"
+ " <namespace uri=\"urn:a\"/>\n"
+ " <prefix value=\"a\"/>\n\n"
+ " <extension name=\"e\">\n"
+ " <argument name=\"name\"/>\n"
+ " </extension>\n\n"
+ " <a:e name=\"aaa\"/>\n"
+ "</module>\n";
+ const char *mod_test_yin, *mod_test_yang;
+ char *printed;
+
+ mod_test_yang = "module b {\n"
+ " namespace \"urn:b\";\n"
+ " prefix b;\n\n"
+ " import a {\n"
+ " prefix a;\n"
+ " }\n\n"
+ " a:e \"xxx\";\n"
+ "}\n";
+ mod_test_yin =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"b\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:b=\"urn:b\"\n"
+ " xmlns:a=\"urn:a\">\n"
+ " <namespace uri=\"urn:b\"/>\n"
+ " <prefix value=\"b\"/>\n"
+ " <import module=\"a\">\n"
+ " <prefix value=\"a\"/>\n"
+ " </import>\n\n"
+ " <a:e name=\"xxx\"/>\n"
+ "</module>\n";
+
+ /* from YANG */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_def_yang);
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_test_yang, LYS_IN_YANG, &mod));
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, mod_test_yang);
+ free(printed);
+
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, mod_test_yin);
+ free(printed);
+
+ assert_non_null(mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL));
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, mod_def_yang);
+ free(printed);
+
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, mod_def_yin);
+ free(printed);
+
+ /* context reset */
+ ly_ctx_destroy(UTEST_LYCTX, NULL);
+ ly_ctx_new(NULL, 0, &UTEST_LYCTX);
+
+ /* from YIN */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_def_yin);
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, &mod));
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, mod_test_yang);
+ free(printed);
+
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, mod_test_yin);
+ free(printed);
+
+ assert_non_null(mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL));
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, mod_def_yang);
+ free(printed);
+
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, mod_def_yin);
+ free(printed);
+}
+
+void
+test_extension_argument_element(void **state)
+{
+ const struct lys_module *mod;
+ const char *mod_def_yang = "module a {\n"
+ " namespace \"urn:a\";\n"
+ " prefix a;\n\n"
+ " extension e {\n"
+ " argument name {\n"
+ " yin-element true;\n"
+ " }\n"
+ " }\n\n"
+ " a:e \"aaa\";\n"
+ "}\n";
+ const char *mod_def_yin =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"a\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:a=\"urn:a\">\n"
+ " <namespace uri=\"urn:a\"/>\n"
+ " <prefix value=\"a\"/>\n\n"
+ " <extension name=\"e\">\n"
+ " <argument name=\"name\">\n"
+ " <yin-element value=\"true\"/>\n"
+ " </argument>\n"
+ " </extension>\n\n"
+ " <a:e>\n"
+ " <a:name>aaa</a:name>\n"
+ " </a:e>\n"
+ "</module>\n";
+ const char *mod_test_yin, *mod_test_yang;
+ char *printed;
+
+ mod_test_yang = "module b {\n"
+ " namespace \"urn:b\";\n"
+ " prefix b;\n\n"
+ " import a {\n"
+ " prefix a;\n"
+ " }\n\n"
+ " a:e \"xxx\";\n"
+ "}\n";
+ mod_test_yin =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"b\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:b=\"urn:b\"\n"
+ " xmlns:a=\"urn:a\">\n"
+ " <namespace uri=\"urn:b\"/>\n"
+ " <prefix value=\"b\"/>\n"
+ " <import module=\"a\">\n"
+ " <prefix value=\"a\"/>\n"
+ " </import>\n\n"
+ " <a:e>\n"
+ " <a:name>xxx</a:name>\n"
+ " </a:e>\n"
+ "</module>\n";
+
+ /* from YANG */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_def_yang);
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_test_yang, LYS_IN_YANG, &mod));
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, mod_test_yang);
+ free(printed);
+
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, mod_test_yin);
+ free(printed);
+
+ assert_non_null(mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL));
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, mod_def_yang);
+ free(printed);
+
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, mod_def_yin);
+ free(printed);
+
+ /* context reset */
+ ly_ctx_destroy(UTEST_LYCTX, NULL);
+ ly_ctx_new(NULL, 0, &UTEST_LYCTX);
+
+ /* from YIN */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_def_yin);
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, &mod));
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, mod_test_yang);
+ free(printed);
+
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, mod_test_yin);
+ free(printed);
+
+ assert_non_null(mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL));
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0));
+ assert_string_equal(printed, mod_def_yang);
+ free(printed);
+
+ assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0));
+ assert_string_equal(printed, mod_def_yin);
+ free(printed);
+
+ /* invalid */
+ mod_test_yang = "module x { namespace \"urn:x\"; prefix x; import a { prefix a; } a:e; }";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yang, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Extension instance \"a:e\" misses argument element \"name\".", "/x:{extension='a:e'}");
+
+ mod_test_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"x\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:x=\"urn:x\"\n"
+ " xmlns:a=\"urn:a\">\n"
+ " <namespace uri=\"urn:x\"/>\n"
+ " <prefix value=\"x\"/>\n"
+ " <import module=\"a\">\n"
+ " <prefix value=\"a\"/>\n"
+ " </import>\n\n"
+ " <a:e/>\n"
+ "</module>\n";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, NULL));
+ CHECK_LOG_CTX("Extension instance \"a:e\" misses argument element \"name\".", "/x:{extension='a:e'}");
+
+ mod_test_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"x\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:x=\"urn:x\"\n"
+ " xmlns:a=\"urn:a\">\n"
+ " <namespace uri=\"urn:x\"/>\n"
+ " <prefix value=\"x\"/>\n"
+ " <import module=\"a\">\n"
+ " <prefix value=\"a\"/>\n"
+ " </import>\n\n"
+ " <a:e name=\"xxx\"/>\n"
+ "</module>\n";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, NULL));
+ CHECK_LOG_CTX("Extension instance \"a:e\" misses argument element \"name\".", "/x:{extension='a:e'}");
+
+ mod_test_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"x\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:x=\"urn:x\"\n"
+ " xmlns:a=\"urn:a\">\n"
+ " <namespace uri=\"urn:x\"/>\n"
+ " <prefix value=\"x\"/>\n"
+ " <import module=\"a\">\n"
+ " <prefix value=\"a\"/>\n"
+ " </import>\n\n"
+ " <a:e>\n"
+ " <x:name>xxx</x:name>\n"
+ " </a:e>\n"
+ "</module>\n";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, NULL));
+ CHECK_LOG_CTX("Extension instance \"a:e\" element and its argument element \"name\" are expected in the same namespace, but they differ.",
+ "/x:{extension='a:e'}");
+
+ mod_test_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<module name=\"x\"\n"
+ " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n"
+ " xmlns:x=\"urn:x\"\n"
+ " xmlns:a=\"urn:a\">\n"
+ " <namespace uri=\"urn:x\"/>\n"
+ " <prefix value=\"x\"/>\n"
+ " <import module=\"a\">\n"
+ " <prefix value=\"a\"/>\n"
+ " </import>\n\n"
+ " <a:e>\n"
+ " <a:value>xxx</a:value>\n"
+ " </a:e>\n"
+ "</module>\n";
+ assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, NULL));
+ CHECK_LOG_CTX("Extension instance \"a:e\" expects argument element \"name\" as its first XML child, but \"value\" element found.",
+ "/x:{extension='a:e'}");
+
+}
diff --git a/tests/utests/utests.h b/tests/utests/utests.h
index b57df7b..1ed4fb4 100644
--- a/tests/utests/utests.h
+++ b/tests/utests/utests.h
@@ -427,16 +427,14 @@
* @param[in] NODE pointer to lysp_ext_instance variable
* @param[in] ARGUMENT expected optional value of the extension's argument
* @param[in] CHILD 0 -> node doesnt have child, 1 -> node have children
- * @param[in] COMPILED 0 -> compiled data dosnt exists, 1 -> compiled data exists
* @param[in] PARENT_STMT expected value identifying placement of the extension instance
* @param[in] PARENT_STMT_INDEX expected indentifi index
* @param[in] FORMAT expected format
*/
-#define CHECK_LYSP_EXT_INSTANCE(NODE, ARGUMENT, CHILD, COMPILED, PARENT_STMT, PARENT_STMT_INDEX, NAME, FORMAT) \
+#define CHECK_LYSP_EXT_INSTANCE(NODE, ARGUMENT, CHILD, PARENT_STMT, PARENT_STMT_INDEX, NAME, FORMAT) \
assert_non_null(NODE); \
CHECK_STRING((NODE)->argument, ARGUMENT); \
CHECK_POINTER((NODE)->child, CHILD); \
- CHECK_POINTER((NODE)->compiled, COMPILED); \
/*assert_int_equal((NODE)->flags, LYS_INTERNAL);*/ \
assert_int_equal((NODE)->parent_stmt, PARENT_STMT); \
assert_int_equal((NODE)->parent_stmt_index, PARENT_STMT_INDEX); \