/
githubmirror
/
libxmlb
Обзор
Документация
Войти
/
githubmirror
/
libxmlb
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/xb-self-test.c
3 426 строк
109 KB
Richard Hughes
Fix a potential out of bounds read when building a XbBuilderNode
31 июл 2026, 14:36
31 июл 2026, 14:36
d9dbfb1
Код
Авторство
О чём код?
/* * Copyright 2018 Richard Hughes <richard@hughsie.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include <gio/gio.h> #include <locale.h> #include "xb-builder-node.h" #include "xb-builder.h" #include "xb-common-private.h" #include "xb-machine.h" #include "xb-node-query.h" #include "xb-opcode-private.h" #include "xb-opcode.h" #include "xb-silo-export.h" #include "xb-silo-private.h" #include "xb-silo-query-private.h" #include "xb-stack-private.h" #include "xb-string-private.h" static GMainLoop *_test_loop = NULL; static guint _test_loop_timeout_id = 0; #define XB_SELF_TEST_INOTIFY_TIMEOUT 10000 /* ms */ static gboolean xb_test_hang_check_cb(gpointer user_data) { g_main_loop_quit(_test_loop); _test_loop_timeout_id = 0; return G_SOURCE_REMOVE; } static void xb_test_loop_run_with_timeout(guint timeout_ms) { g_assert(_test_loop_timeout_id == 0); g_assert(_test_loop == NULL); _test_loop = g_main_loop_new(NULL, FALSE); _test_loop_timeout_id = g_timeout_add(timeout_ms, xb_test_hang_check_cb, NULL); g_main_loop_run(_test_loop); } static void xb_test_loop_quit(void) { g_clear_handle_id(&_test_loop_timeout_id, g_source_remove); if (_test_loop != NULL) { g_main_loop_quit(_test_loop); g_main_loop_unref(_test_loop); _test_loop = NULL; } } static gboolean xb_test_import_xml(XbBuilder *self, const gchar *xml, GError **error) { g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_return_val_if_fail(XB_IS_BUILDER(self), FALSE); g_return_val_if_fail(xml != NULL, FALSE); /* add source */ if (!xb_builder_source_load_xml(source, xml, XB_BUILDER_SOURCE_FLAG_NONE, error)) return FALSE; /* success */ xb_builder_import_source(self, source); return TRUE; } static void xb_stack_func(void) { XbOpcode *op1, *op2, *op3, *op4; g_auto(XbOpcode) op1_popped = XB_OPCODE_INIT(); g_auto(XbOpcode) op2_popped = XB_OPCODE_INIT(); g_auto(XbOpcode) op3_popped = XB_OPCODE_INIT(); g_autoptr(XbStack) stack = xb_stack_new(3); /* push three opcodes */ g_assert_true(xb_stack_push(stack, &op3, NULL)); xb_opcode_text_init(op3, "dave"); g_assert_true(xb_stack_push(stack, &op2, NULL)); xb_opcode_integer_init(op2, 1); g_assert_true(xb_stack_push(stack, &op1, NULL)); xb_opcode_func_init(op1, 0); g_assert_false(xb_stack_push(stack, &op4, NULL)); g_assert_null(op4); /* pop the same opcodes */ g_assert_true(xb_stack_pop(stack, &op1_popped, NULL)); g_assert_cmpint(xb_opcode_get_kind(&op1_popped), ==, XB_OPCODE_KIND_FUNCTION); g_assert_true(xb_stack_pop(stack, &op2_popped, NULL)); g_assert_cmpint(xb_opcode_get_kind(&op2_popped), ==, XB_OPCODE_KIND_INTEGER); g_assert_cmpuint(xb_opcode_get_val(&op2_popped), ==, 1); g_assert_true(xb_stack_pop(stack, &op3_popped, NULL)); g_assert_cmpint(xb_opcode_get_kind(&op3_popped), ==, XB_OPCODE_KIND_TEXT); g_assert_cmpstr(xb_opcode_get_str(&op3_popped), ==, "dave"); /* re-add one opcode */ g_assert_true(xb_stack_push(stack, &op4, NULL)); xb_opcode_text_init(op4, "dave again"); g_assert_nonnull(op4); /* finish, cleaning up the stack properly... */ } static void xb_stack_peek_func(void) { XbOpcode *op1, *op2, *op3; g_autoptr(XbStack) stack = xb_stack_new(3); /* push three opcodes */ g_assert_true(xb_stack_push(stack, &op1, NULL)); xb_opcode_func_init(op1, 0); g_assert_true(xb_stack_push(stack, &op2, NULL)); xb_opcode_integer_init(op2, 1); g_assert_true(xb_stack_push(stack, &op3, NULL)); xb_opcode_text_init(op3, "dave"); /* peek the same opcodes */ g_assert_true(xb_stack_peek_head(stack) == op1); g_assert_true(xb_stack_peek_tail(stack) == op3); g_assert_true(xb_stack_peek(stack, 0) == op1); g_assert_true(xb_stack_peek(stack, 1) == op2); g_assert_true(xb_stack_peek(stack, 2) == op3); } static void xb_common_union_func(void) { g_autoptr(GString) xpath = g_string_new(NULL); xb_string_append_union(xpath, "components/component"); g_assert_cmpstr(xpath->str, ==, "components/component"); xb_string_append_union(xpath, "applications/application"); g_assert_cmpstr(xpath->str, ==, "components/component|applications/application"); } static void xb_common_func(void) { g_assert_true(xb_string_search("gimp", "gimp")); g_assert_true(xb_string_search("GIMP", "gimp")); g_assert_true(xb_string_search("The GIMP", "gimp")); g_assert_true(xb_string_search("The GIMP Editor", "gimp")); g_assert_false(xb_string_search("gimp", "")); g_assert_false(xb_string_search("gimp", "imp")); g_assert_false(xb_string_search("the gimp editor", "imp")); g_assert_true(xb_string_token_valid("the")); g_assert_false(xb_string_token_valid(NULL)); g_assert_false(xb_string_token_valid("")); g_assert_false(xb_string_token_valid("a")); g_assert_false(xb_string_token_valid("ab")); } static void xb_common_content_type_func(void) { struct { const gchar *fn; const gchar *ctype; } items[] = {{"test.desktop", "application/x-desktop"}, {"test.quirk", "text/plain"}, {"test.xml", "application/xml"}, {"test.xml.gz.gz.gz", "application/gzip"}, {"test.xml.xz", "application/x-xz"}, {"test.xml.zst", "application/zstd"}, {NULL, NULL}}; for (guint i = 0; items[i].fn != NULL; i++) { gboolean ret; gsize bufsz = 0; g_autofree gchar *buf = NULL; g_autofree gchar *ctype = NULL; g_autofree gchar *fn = g_test_build_filename(G_TEST_DIST, items[i].fn, NULL); g_autoptr(GError) error = NULL; ret = g_file_get_contents(fn, &buf, &bufsz, &error); g_assert_no_error(error); g_assert_true(ret); ctype = xb_content_type_guess(fn, (const guchar *)buf, bufsz); g_assert_cmpstr(ctype, ==, items[i].ctype); } } static void xb_common_searchv_func(void) { const gchar *haystack[] = {"these", "words", "ready", NULL}; const gchar *found[] = {"xxx", "wor", "yyy", NULL}; const gchar *unfound1[] = {"xxx", "yyy", NULL}; const gchar *unfound2[] = {"ords", NULL}; g_assert_true(xb_string_searchv(haystack, found)); g_assert_false(xb_string_searchv(haystack, unfound1)); g_assert_false(xb_string_searchv(haystack, unfound2)); } static void xb_opcodes_kind_func(void) { g_auto(XbOpcode) op1 = XB_OPCODE_INIT(); g_auto(XbOpcode) op2 = XB_OPCODE_INIT(); g_auto(XbOpcode) op3 = XB_OPCODE_INIT(); xb_opcode_func_init(&op1, 0); xb_opcode_integer_init(&op2, 1); xb_opcode_text_init(&op3, "dave"); /* check kind */ g_assert_cmpint(xb_opcode_get_kind(&op1), ==, XB_OPCODE_KIND_FUNCTION); g_assert_cmpint(xb_opcode_get_kind(&op2), ==, XB_OPCODE_KIND_INTEGER); g_assert_cmpint(xb_opcode_get_kind(&op3), ==, XB_OPCODE_KIND_TEXT); /* to and from string */ g_assert_cmpint(xb_opcode_kind_from_string("TEXT"), ==, XB_OPCODE_KIND_TEXT); g_assert_cmpint(xb_opcode_kind_from_string("FUNC"), ==, XB_OPCODE_KIND_FUNCTION); g_assert_cmpint(xb_opcode_kind_from_string("INTE"), ==, XB_OPCODE_KIND_INTEGER); g_assert_cmpint(xb_opcode_kind_from_string("dave"), ==, XB_OPCODE_KIND_UNKNOWN); g_assert_cmpstr(xb_opcode_kind_to_string(XB_OPCODE_KIND_TEXT), ==, "TEXT"); g_assert_cmpstr(xb_opcode_kind_to_string(XB_OPCODE_KIND_FUNCTION), ==, "FUNC"); g_assert_cmpstr(xb_opcode_kind_to_string(XB_OPCODE_KIND_INTEGER), ==, "INTE"); g_assert_cmpstr(xb_opcode_kind_to_string(XB_OPCODE_KIND_UNKNOWN), ==, NULL); /* integer compare */ g_assert_false(xb_opcode_cmp_val(&op1)); g_assert_true(xb_opcode_cmp_val(&op2)); g_assert_false(xb_opcode_cmp_val(&op3)); /* string compare */ g_assert_false(xb_opcode_cmp_str(&op1)); g_assert_false(xb_opcode_cmp_str(&op2)); g_assert_true(xb_opcode_cmp_str(&op3)); } static void xb_predicate_func(void) { g_autoptr(XbSilo) silo = xb_silo_new(); struct { const gchar *pred; const gchar *str; } tests[] = {{"'a'='b'", "'a','b',eq()"}, {"@a='b'", "'a',attr(),'b',eq()"}, {"@a=='b'", "'a',attr(),'b',eq()"}, {"'a'<'b'", "'a','b',lt()"}, {"999>=123", "999,123,ge()"}, {"not(0)", "0^1,not()"}, {"@a", "'a',attr(),'(null)',ne()"}, {"not(@a)", "'a'^1,attr()^1,not()"}, {"'a'=", "'a',eq()"}, {"='b'", "'b',eq()"}, {"999=\'b\'", "999,'b',eq()"}, {"text()=\'b\'", "text(),'b',eq()"}, {"last()", "last()"}, {"text()~='beef'", "text(),'beef'[beef],search()"}, {"@type~='dead'", "'type',attr(),'dead',search()"}, {"2", "2,position(),eq()"}, {"text()=lower-case('firefox')", "text(),'firefox'^1,lower-case(),eq()"}, {"$'a'=$'b'", "$'a',$'b',eq()"}, {"('a'='b')&&('c'='d')", "'a'^1,'b'^1,eq()^1,'c'^1,'d'^1,eq()^1,and()"}, {"text()==('a','b','c')", "text(),'c'^1,'b'^1,'a'^1,in()"}, /* sentinel */ {NULL, NULL}}; const gchar *invalid[] = {"text(", "text((((((((((((((((((((text()))))))))))))))))))))", NULL}; xb_machine_set_debug_flags(xb_silo_get_machine(silo), XB_MACHINE_DEBUG_FLAG_SHOW_STACK | XB_MACHINE_DEBUG_FLAG_SHOW_PARSING); for (guint i = 0; tests[i].pred != NULL; i++) { g_autofree gchar *str = NULL; g_autoptr(GError) error = NULL; g_autoptr(XbStack) opcodes = NULL; g_debug("testing %s", tests[i].pred); opcodes = xb_machine_parse_full(xb_silo_get_machine(silo), tests[i].pred, -1, XB_MACHINE_PARSE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_nonnull(opcodes); str = xb_stack_to_string(opcodes); g_assert_nonnull(str); g_assert_cmpstr(str, ==, tests[i].str); } for (guint i = 0; invalid[i] != NULL; i++) { g_autoptr(GError) error = NULL; g_autoptr(XbStack) opcodes = NULL; g_debug("testing %s", invalid[i]); opcodes = xb_machine_parse_full(xb_silo_get_machine(silo), invalid[i], -1, XB_MACHINE_PARSE_FLAG_NONE, &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA); g_assert_null(opcodes); } } static void xb_predicate_optimize_func(void) { g_autoptr(XbSilo) silo = xb_silo_new(); struct { const gchar *pred; const gchar *str; } tests[] = {{"@a='b'", "'a',attr(),'b',eq()"}, {"'a'<'b'", "True"}, /* success! */ {"999>=123", "True"}, /* success! */ {"not(0)", "True"}, /* success! */ {"lower-case('Fire')", "'fire'"}, {"upper-case('Τάχιστη')", "'ΤΆΧΙΣΤΗ'"}, {"upper-case(lower-case('Fire'))", "'FIRE'"}, /* 2nd pass */ {"text()==('a','b','c')", "text(),'c'^1,'b'^1,'a'^1,in()"}, /* sentinel */ {NULL, NULL}}; const gchar *invalid[] = {"'a'='b'", "123>=999", "not(1)", NULL}; xb_machine_set_debug_flags(xb_silo_get_machine(silo), XB_MACHINE_DEBUG_FLAG_SHOW_STACK | XB_MACHINE_DEBUG_FLAG_SHOW_OPTIMIZER); for (guint i = 0; tests[i].pred != NULL; i++) { g_autofree gchar *str = NULL; g_autoptr(GError) error = NULL; g_autoptr(XbStack) opcodes = NULL; g_debug("testing %s", tests[i].pred); opcodes = xb_machine_parse_full(xb_silo_get_machine(silo), tests[i].pred, -1, XB_MACHINE_PARSE_FLAG_OPTIMIZE, &error); g_assert_no_error(error); g_assert_nonnull(opcodes); str = xb_stack_to_string(opcodes); g_assert_nonnull(str); g_assert_cmpstr(str, ==, tests[i].str); } for (guint i = 0; invalid[i] != NULL; i++) { g_autoptr(GError) error = NULL; g_autoptr(XbStack) opcodes = NULL; opcodes = xb_machine_parse_full(xb_silo_get_machine(silo), invalid[i], -1, XB_MACHINE_PARSE_FLAG_OPTIMIZE, &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA); g_assert_null(opcodes); } } static void xb_builder_func(void) { g_autofree gchar *str = NULL; g_autofree gchar *xml_new = NULL; g_autoptr(GBytes) bytes = NULL; g_autoptr(GError) error = NULL; g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<components origin=\"lvfs\">\n" " <header type=\"<&>\">\n" " <csum type=\"sha1\">dead</csum>\n" " </header>\n" " <component type=\"desktop\" attr=\"value\">\n" " <id>gimp.desktop</id>\n" " <name>GIMP & Friendẞ</name>\n" " <id>org.gnome.Gimp.desktop</id>\n" " </component>\n" " <component type=\"desktop\">\n" " <id>gnome-software.desktop</id>\n" " </component>\n" " <component type=\"firmware\">\n" " <id>org.hughski.ColorHug2.firmware</id>\n" " <requires>\n" " <bootloader>1.2.3</bootloader>\n" " </requires>\n" " </component>\n" "</components>\n"; /* import from XML */ silo = xb_silo_new_from_xml(xml, &error); g_assert_no_error(error); g_assert_nonnull(silo); g_assert_true(xb_silo_is_valid(silo)); /* convert back to XML */ str = xb_silo_to_string(silo, &error); g_assert_no_error(error); g_assert_nonnull(str); g_debug("\n%s", str); xml_new = xb_silo_export(silo, XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE | XB_NODE_EXPORT_FLAG_FORMAT_INDENT, &error); g_assert_no_error(error); g_assert_nonnull(xml_new); g_print("%s", xml_new); g_assert_cmpstr(xml, ==, xml_new); /* check size */ bytes = xb_silo_get_bytes(silo); g_assert_cmpint(g_bytes_get_size(bytes), ==, 628); } static void xb_builder_ensure_invalidate_cb(XbSilo *silo, GParamSpec *pspec, gpointer user_data) { guint *invalidate_cnt = (guint *)user_data; (*invalidate_cnt)++; xb_test_loop_quit(); } static GInputStream * xb_builder_custom_mime_cb(XbBuilderSource *self, XbBuilderSourceCtx *ctx, gpointer user_data, GCancellable *cancellable, GError **error) { gchar *xml = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" "<component type=\"desktop\">" "<id>%s</id></component>", xb_builder_source_ctx_get_filename(ctx)); return g_memory_input_stream_new_from_data(g_steal_pointer(&xml), -1, g_free); } static void xb_builder_custom_mime_func(void) { gboolean ret; g_autofree gchar *xml = NULL; g_autofree gchar *tmp_desktop = g_build_filename(g_get_tmp_dir(), "temp.desktop", NULL); g_autofree gchar *tmp_xmlb = g_build_filename(g_get_tmp_dir(), "temp.xmlb", NULL); g_autoptr(GError) error = NULL; g_autoptr(GFile) file_desktop = NULL; g_autoptr(GFile) file = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbSilo) silo = NULL; /* add support for desktop files */ xb_builder_source_add_adapter(source, "application/x-desktop", xb_builder_custom_mime_cb, NULL, NULL); /* import a source file */ ret = g_file_set_contents(tmp_desktop, "[Desktop Entry]", -1, &error); g_assert_no_error(error); g_assert_true(ret); file_desktop = g_file_new_for_path(tmp_desktop); ret = xb_builder_source_load_file(source, file_desktop, XB_BUILDER_SOURCE_FLAG_WATCH_FILE, NULL, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_import_source(builder, source); file = g_file_new_for_path(tmp_xmlb); silo = xb_builder_ensure(builder, file, XB_BUILDER_COMPILE_FLAG_WATCH_BLOB, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* check contents */ xml = xb_silo_export(silo, XB_NODE_EXPORT_FLAG_NONE, &error); g_assert_no_error(error); g_assert_nonnull(xml); g_print("%s", xml); g_assert_cmpstr("<component type=\"desktop\">" "<id>temp.desktop</id>" "</component>", ==, xml); } static void xb_builder_source_lzma_func(void) { gboolean ret; g_autofree gchar *path = NULL; g_autofree gchar *tmp_xmlb = g_build_filename(g_get_tmp_dir(), "temp.xmlb", NULL); g_autoptr(GError) error = NULL; g_autoptr(GFile) file = NULL; g_autoptr(GFile) file_src = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbSilo) silo = NULL; #ifndef HAVE_LZMA /* not supported */ g_test_skip("compiled without -Dlzma"); return; #endif /* import a source file */ path = g_test_build_filename(G_TEST_DIST, "test.xml.xz", NULL); file_src = g_file_new_for_path(path); if (!g_file_query_exists(file_src, NULL)) { g_test_skip("does not work in subproject test"); return; } ret = xb_builder_source_load_file(source, file_src, XB_BUILDER_SOURCE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_import_source(builder, source); file = g_file_new_for_path(tmp_xmlb); silo = xb_builder_ensure(builder, file, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); } static void xb_builder_source_zstd_func(void) { gboolean ret; g_autofree gchar *path = NULL; g_autofree gchar *tmp_xmlb = g_build_filename(g_get_tmp_dir(), "temp.xmlb", NULL); g_autoptr(GError) error = NULL; g_autoptr(GFile) file = NULL; g_autoptr(GFile) file_src = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbSilo) silo = NULL; #ifndef HAVE_ZSTD /* not supported */ g_test_skip("compiled without -Dzstd"); return; #endif /* import a source file */ path = g_test_build_filename(G_TEST_DIST, "test.xml.zst", NULL); file_src = g_file_new_for_path(path); if (!g_file_query_exists(file_src, NULL)) { g_test_skip("does not work in subproject test"); return; } ret = xb_builder_source_load_file(source, file_src, XB_BUILDER_SOURCE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_import_source(builder, source); file = g_file_new_for_path(tmp_xmlb); silo = xb_builder_ensure(builder, file, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); } static void xb_builder_chained_adapters_func(void) { gboolean ret; g_autofree gchar *xml = NULL; g_autofree gchar *path = NULL; g_autofree gchar *tmp_xmlb = g_build_filename(g_get_tmp_dir(), "temp.xmlb", NULL); g_autoptr(GError) error = NULL; g_autoptr(GFile) file_src = NULL; g_autoptr(GFile) file = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbSilo) silo = NULL; /* import a source file */ path = g_test_build_filename(G_TEST_DIST, "test.xml.gz.gz.gz", NULL); file_src = g_file_new_for_path(path); if (!g_file_query_exists(file_src, NULL)) { g_test_skip("does not work in subproject test"); return; } ret = xb_builder_source_load_file(source, file_src, XB_BUILDER_SOURCE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_import_source(builder, source); file = g_file_new_for_path(tmp_xmlb); silo = xb_builder_ensure(builder, file, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* check contents */ xml = xb_silo_export(silo, XB_NODE_EXPORT_FLAG_NONE, &error); g_assert_no_error(error); g_assert_nonnull(xml); g_print("%s", xml); g_assert_cmpstr("<id>Hello world!</id>", ==, xml); } static void xb_builder_ensure_watch_source_func(void) { gboolean ret; guint invalidate_cnt = 0; g_autoptr(GError) error = NULL; g_autoptr(GFile) file = NULL; g_autoptr(GFile) file_xml = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbSilo) silo = NULL; g_autofree gchar *tmp_xml = g_build_filename(g_get_tmp_dir(), "temp.xml", NULL); g_autofree gchar *tmp_xmlb = g_build_filename(g_get_tmp_dir(), "temp.xmlb", NULL); #ifdef _WIN32 /* no inotify */ g_test_skip("inotify does not work on mingw"); return; #endif /* import a source file */ ret = g_file_set_contents(tmp_xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<id>gimp</id>", -1, &error); g_assert_no_error(error); g_assert_true(ret); file_xml = g_file_new_for_path(tmp_xml); ret = xb_builder_source_load_file(source, file_xml, XB_BUILDER_SOURCE_FLAG_WATCH_FILE, NULL, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_import_source(builder, source); file = g_file_new_for_path(tmp_xmlb); g_file_delete(file, NULL, NULL); silo = xb_builder_ensure(builder, file, XB_BUILDER_COMPILE_FLAG_WATCH_BLOB, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); g_assert_true(xb_silo_is_valid(silo)); g_signal_connect(silo, "notify::valid", G_CALLBACK(xb_builder_ensure_invalidate_cb), &invalidate_cnt); /* change source file */ ret = g_file_set_contents(tmp_xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<id>inkscape</id>", -1, &error); g_assert_no_error(error); g_assert_true(ret); xb_test_loop_run_with_timeout(XB_SELF_TEST_INOTIFY_TIMEOUT); g_assert_false(xb_silo_is_valid(silo)); g_assert_cmpint(invalidate_cnt, ==, 1); g_assert_false(xb_silo_is_valid(silo)); } static void xb_builder_ensure_func(void) { gboolean ret; guint invalidate_cnt = 0; g_autofree gchar *bytes1str = NULL; g_autofree gchar *bytes2str = NULL; g_autofree gchar *bytes3str = NULL; g_autofree gchar *tmp_xmlb = g_build_filename(g_get_tmp_dir(), "temp.xmlb", NULL); g_autoptr(GBytes) bytes1 = NULL; g_autoptr(GBytes) bytes2 = NULL; g_autoptr(GBytes) bytes3 = NULL; g_autoptr(GError) error = NULL; g_autoptr(GFile) file = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<components origin=\"lvfs\">\n" " <header type=\"<&>\">\n" " <csum type=\"sha1\">dead</csum>\n" " </header>\n" " <component type=\"desktop\" attr=\"value\">\n" " <id>gimp.desktop</id>\n" " <name>GIMP & Friendẞ</name>\n" " <id>org.gnome.Gimp.desktop</id>\n" " </component>\n" " <component type=\"desktop\">\n" " <id>gnome-software.desktop</id>\n" " </component>\n" " <component type=\"firmware\">\n" " <id>org.hughski.ColorHug2.firmware</id>\n" " <requires>\n" " <bootloader>1.2.3</bootloader>\n" " </requires>\n" " </component>\n" "</components>\n"; #ifdef _WIN32 /* no inotify */ g_test_skip("inotify does not work on mingw"); return; #endif #ifdef __sun /* no inotify */ g_test_skip("missing support for inotify"); return; #endif /* import some XML */ xb_builder_set_profile_flags(builder, XB_SILO_PROFILE_FLAG_DEBUG); ret = xb_test_import_xml(builder, xml, &error); g_assert_no_error(error); g_assert_true(ret); /* create file if it does not exist */ file = g_file_new_for_path(tmp_xmlb); g_file_delete(file, NULL, NULL); silo = xb_builder_ensure(builder, file, XB_BUILDER_COMPILE_FLAG_WATCH_BLOB, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); g_signal_connect(silo, "notify::valid", G_CALLBACK(xb_builder_ensure_invalidate_cb), &invalidate_cnt); g_assert_cmpint(invalidate_cnt, ==, 0); bytes1 = xb_silo_get_bytes(silo); bytes1str = g_compute_checksum_for_bytes(G_CHECKSUM_SHA1, bytes1); /* recreate file if it is invalid */ ret = g_file_replace_contents(file, "dave", 4, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error); g_assert_no_error(error); g_assert_true(ret); xb_test_loop_run_with_timeout(XB_SELF_TEST_INOTIFY_TIMEOUT); g_assert_false(xb_silo_is_valid(silo)); g_assert_cmpint(invalidate_cnt, ==, 1); g_clear_object(&silo); silo = xb_builder_ensure(builder, file, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); g_assert_true(xb_silo_is_valid(silo)); bytes2 = xb_silo_get_bytes(silo); bytes2str = g_compute_checksum_for_bytes(G_CHECKSUM_SHA1, bytes2); g_assert_cmpstr(bytes1str, !=, bytes2str); g_clear_object(&silo); /* don't recreate file if perfectly valid */ silo = xb_builder_ensure(builder, file, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); g_assert_true(xb_silo_is_valid(silo)); bytes3 = xb_silo_get_bytes(silo); bytes3str = g_compute_checksum_for_bytes(G_CHECKSUM_SHA1, bytes3); g_assert_cmpstr(bytes2str, ==, bytes3str); g_clear_object(&silo); g_clear_object(&builder); /* don't re-create for a new builder with the same XML added */ builder = xb_builder_new(); ret = xb_test_import_xml(builder, xml, &error); g_assert_no_error(error); g_assert_true(ret); silo = xb_builder_ensure(builder, file, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); } static gboolean xb_builder_error_cb(XbBuilderFixup *self, XbBuilderNode *bn, gpointer user_data, GError **error) { g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_BUSY, "engine was busy"); return FALSE; } static void xb_builder_node_vfunc_error_func(void) { gboolean ret; g_autoptr(GError) error = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderFixup) fixup = NULL; g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbSilo) silo = NULL; /* add fixup */ fixup = xb_builder_fixup_new("AlwaysError", xb_builder_error_cb, NULL, NULL); xb_builder_source_add_fixup(source, fixup); /* import some XML */ ret = xb_builder_source_load_xml(source, "<id>gimp.desktop</id>", XB_BUILDER_SOURCE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_import_source(builder, source); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_BUSY); g_assert_null(silo); } static gboolean xb_builder_ignore_cb(XbBuilderFixup *self, XbBuilderNode *bn, gpointer user_data, GError **error) { if (xb_builder_node_get_element(bn) != NULL) xb_builder_node_add_flag(bn, XB_BUILDER_NODE_FLAG_IGNORE); return TRUE; } static void xb_builder_node_vfunc_ignore_func(void) { XbNodeChildIter iter; gboolean ret; const gchar *element; g_autoptr(GError) error = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderFixup) fixup = NULL; g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbBuilderNode) info = NULL; g_autoptr(XbSilo) silo = NULL; g_autoptr(XbNode) n = NULL; g_autoptr(XbNode) c = NULL; g_autoptr(XbNode) c2 = NULL; /* add fixup */ fixup = xb_builder_fixup_new("AlwaysIgnore", xb_builder_ignore_cb, NULL, NULL); xb_builder_source_add_fixup(source, fixup); /* import some XML with metadata */ ret = xb_builder_source_load_xml(source, "<component><id>gimp.desktop</id></component>", XB_BUILDER_SOURCE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); info = xb_builder_node_insert(NULL, "info", NULL); xb_builder_node_insert_text(info, "foo", "bar", NULL); xb_builder_source_set_info(source, info); xb_builder_import_source(builder, source); /* compile */ silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); n = xb_silo_get_root(silo); g_assert_nonnull(n); element = xb_node_get_element(n); g_assert_null(element); c = xb_node_get_child(n); g_assert_null(c); c = xb_node_get_next(n); g_assert_null(c); xb_node_child_iter_init(&iter, n); ret = xb_node_child_iter_next(&iter, &c2); g_assert_false(ret); g_assert_null(c2); } static gboolean xb_builder_upgrade_appstream_cb(XbBuilderFixup *self, XbBuilderNode *bn, gpointer user_data, GError **error) { if (g_strcmp0(xb_builder_node_get_element(bn), "application") == 0) { g_autoptr(XbBuilderNode) id = xb_builder_node_get_child(bn, "id", NULL); g_autofree gchar *kind = NULL; if (id != NULL) { kind = g_strdup(xb_builder_node_get_attr(id, "type")); xb_builder_node_remove_attr(id, "type"); } if (kind != NULL) xb_builder_node_set_attr(bn, "type", kind); xb_builder_node_set_element(bn, "component"); } else if (g_strcmp0(xb_builder_node_get_element(bn), "metadata") == 0) { xb_builder_node_set_element(bn, "custom"); } return TRUE; } static void xb_builder_node_vfunc_func(void) { gboolean ret; g_autofree gchar *xml2 = NULL; g_autoptr(GError) error = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderFixup) fixup = NULL; g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbSilo) silo = NULL; const gchar *xml = " <application>\n" " <id type=\"desktop\">gimp.desktop</id>\n" " </application>\n"; /* add fixup */ fixup = xb_builder_fixup_new("AppStreamUpgrade", xb_builder_upgrade_appstream_cb, NULL, NULL); xb_builder_source_add_fixup(source, fixup); /* import some XML */ ret = xb_builder_source_load_xml(source, xml, XB_BUILDER_SOURCE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_import_source(builder, source); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* check the XML */ xml2 = xb_silo_export(silo, XB_NODE_EXPORT_FLAG_NONE, &error); g_assert_no_error(error); g_assert_nonnull(xml2); g_print("%s\n", xml2); g_assert_cmpstr("<component type=\"desktop\">" "<id>gimp.desktop</id>" "</component>", ==, xml2); } static gboolean xb_builder_fixup_ignore_node_cb(XbBuilderFixup *self, XbBuilderNode *bn, gpointer user_data, GError **error) { if (g_strcmp0(xb_builder_node_get_element(bn), "component") == 0) { g_autoptr(XbBuilderNode) id = xb_builder_node_get_child(bn, "id", NULL); if (id != NULL && g_strcmp0(xb_builder_node_get_text(id), "gimp.desktop") == 0) xb_builder_node_add_flag(bn, XB_BUILDER_NODE_FLAG_IGNORE); } else { g_debug("ignoring %s", xb_builder_node_get_element(bn)); } return TRUE; } static void xb_builder_node_vfunc_remove_func(void) { gboolean ret; g_autofree gchar *xml2 = NULL; g_autoptr(GError) error = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderFixup) fixup = NULL; g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbSilo) silo = NULL; const gchar *xml = " <components>\n" " <component>\n" " <id>gimp.desktop</id>\n" " </component>\n" " <component>\n" " <id>inkscape.desktop</id>\n" " </component>\n" " </components>\n"; /* add fixup */ fixup = xb_builder_fixup_new("RemoveGimp", xb_builder_fixup_ignore_node_cb, NULL, NULL); xb_builder_source_add_fixup(source, fixup); /* import some XML */ ret = xb_builder_source_load_xml(source, xml, XB_BUILDER_SOURCE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_import_source(builder, source); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* check the XML */ xml2 = xb_silo_export(silo, XB_NODE_EXPORT_FLAG_NONE, &error); g_assert_no_error(error); g_assert_nonnull(xml2); g_print("%s\n", xml2); g_assert_cmpstr("<components>" "<component>" "<id>inkscape.desktop</id>" "</component>" "</components>", ==, xml2); } static gboolean xb_builder_fixup_root_node_only_cb(XbBuilderFixup *self, XbBuilderNode *bn, gpointer user_data, GError **error) { g_debug(">%s<", xb_builder_node_get_element(bn)); g_assert_cmpstr(xb_builder_node_get_element(bn), ==, NULL); return TRUE; } static void xb_builder_node_vfunc_depth_func(void) { gboolean ret; g_autoptr(GError) error = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderFixup) fixup = NULL; g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbSilo) silo = NULL; const gchar *xml = " <components>\n" " <component>\n" " <id>gimp.desktop</id>\n" " </component>\n" " </components>\n"; /* add fixup */ fixup = xb_builder_fixup_new("OnlyRoot", xb_builder_fixup_root_node_only_cb, NULL, NULL); xb_builder_fixup_set_max_depth(fixup, 0); xb_builder_source_add_fixup(source, fixup); /* import some XML */ ret = xb_builder_source_load_xml(source, xml, XB_BUILDER_SOURCE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_import_source(builder, source); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); } static void xb_builder_ignore_invalid_func(void) { gboolean ret; g_autofree gchar *xml2 = NULL; g_autoptr(GError) error = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbSilo) silo = NULL; /* import some correct XML */ ret = xb_test_import_xml(builder, "<book><id>foobar</id></book>", &error); g_assert_no_error(error); g_assert_true(ret); /* import some incorrect XML */ ret = xb_test_import_xml(builder, "<book><id>foobar</id>", &error); g_assert_no_error(error); g_assert_true(ret); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_IGNORE_INVALID, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* check the XML */ xml2 = xb_silo_export(silo, XB_NODE_EXPORT_FLAG_NONE, &error); g_assert_no_error(error); g_assert_nonnull(xml2); g_print("%s\n", xml2); g_assert_cmpstr("<book><id>foobar</id></book>", ==, xml2); } static void xb_builder_empty_func(void) { gboolean ret; g_autofree gchar *str = NULL; g_autofree gchar *xml = NULL; g_autoptr(GBytes) bytes = NULL; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) results = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbSilo) silo2 = xb_silo_new(); g_autoptr(XbSilo) silo = NULL; /* import from XML */ silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); g_assert_true(xb_silo_is_valid(silo)); /* check size */ bytes = xb_silo_get_bytes(silo); g_assert_cmpint(g_bytes_get_size(bytes), ==, 40); /* try to dump */ str = xb_silo_to_string(silo, &error); g_assert_no_error(error); g_assert_nonnull(str); g_debug("%s", str); /* try to export */ xml = xb_silo_export(silo, XB_NODE_EXPORT_FLAG_NONE, &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); g_assert_null(xml); g_clear_error(&error); /* try to query empty silo */ results = xb_silo_query(silo, "components/component", 0, &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); g_assert_null(results); g_clear_error(&error); /* load blob */ g_assert_nonnull(bytes); ret = xb_silo_load_from_bytes(silo2, bytes, 0, &error); g_assert_no_error(error); g_assert_true(ret); } static void xb_xpath_node_func(void) { g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) results = NULL; g_autoptr(XbNode) n = NULL; g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<components origin=\"lvfs\">\n" " <component type=\"desktop\">\n" " <id>gimp.desktop</id>\n" " <id>org.gnome.Gimp.desktop</id>\n" " </component>\n" " <component type=\"firmware\">\n" " <id>org.hughski.ColorHug2.firmware</id>\n" " </component>\n" "</components>\n"; /* import from XML */ silo = xb_silo_new_from_xml(xml, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* get node */ n = xb_silo_query_first(silo, "components/component", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_attr(n, "type"), ==, "desktop"); /* query with text opcodes */ results = xb_node_query(n, "id", 0, &error); g_assert_no_error(error); g_assert_nonnull(results); g_assert_cmpint(results->len, ==, 2); } static void xb_node_data_func(void) { g_autoptr(GError) error = NULL; g_autoptr(XbNode) n = NULL; g_autoptr(XbSilo) silo = NULL; g_autoptr(GBytes) bytes = g_bytes_new("foo", 4); /* import from XML */ silo = xb_silo_new_from_xml("<id>gimp.desktop</id>", &error); g_assert_no_error(error); g_assert_nonnull(silo); /* get node */ n = xb_silo_query_first(silo, "id", &error); g_assert_no_error(error); g_assert_nonnull(n); xb_node_set_data(n, "store", bytes); xb_node_set_data(n, "store", bytes); g_assert_nonnull(xb_node_get_data(n, "store")); g_assert_null(xb_node_get_data(n, "dave")); } static void xb_node_export_func(void) { g_autoptr(GError) error = NULL; g_autoptr(XbNode) n = NULL; g_autoptr(XbSilo) silo = NULL; g_autofree gchar *xml_default = NULL; g_autofree gchar *xml_collapsed = NULL; /* import from XML */ silo = xb_silo_new_from_xml("<component attr1=\"val1\" attr2=\"val2\"/>", &error); g_assert_no_error(error); g_assert_nonnull(silo); /* get node */ n = xb_silo_query_first(silo, "component", &error); g_assert_no_error(error); g_assert_nonnull(n); /* export default */ xml_default = xb_node_export(n, XB_NODE_EXPORT_FLAG_NONE, &error); g_assert_no_error(error); g_assert_nonnull(xml_default); g_assert_cmpstr(xml_default, ==, "<component attr1=\"val1\" attr2=\"val2\"></component>"); /* export collapsed */ xml_collapsed = xb_node_export(n, XB_NODE_EXPORT_FLAG_COLLAPSE_EMPTY, &error); g_assert_no_error(error); g_assert_nonnull(xml_collapsed); g_assert_cmpstr(xml_collapsed, ==, "<component attr1=\"val1\" attr2=\"val2\" />"); } static void xb_node_export_collapse_func(void) { const gchar *xml = "<components><aaa /><bbb /></components>"; g_autofree gchar *xml_collapsed = NULL; g_autoptr(GError) error = NULL; g_autoptr(XbNode) n = NULL; g_autoptr(XbSilo) silo = NULL; /* import from XML */ silo = xb_silo_new_from_xml(xml, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* export collapsed */ n = xb_silo_get_root(silo); g_assert_nonnull(n); xml_collapsed = xb_node_export(n, XB_NODE_EXPORT_FLAG_COLLAPSE_EMPTY, &error); g_assert_no_error(error); g_assert_cmpstr(xml_collapsed, ==, xml); } static void xb_xpath_parent_subnode_func(void) { g_autofree gchar *xml2 = NULL; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) children = NULL; g_autoptr(XbNode) n = NULL; g_autoptr(XbNode) c = NULL; g_autoptr(XbNode) p = NULL; g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<components origin=\"lvfs\">\n" " <component type=\"desktop\">\n" " <id>gimp.desktop</id>\n" " <id>org.gnome.Gimp.desktop</id>\n" " </component>\n" " <component type=\"firmware\">\n" " <id>org.hughski.ColorHug2.firmware</id>\n" " </component>\n" "</components>\n"; /* import from XML */ silo = xb_silo_new_from_xml(xml, &error); g_assert_no_error(error); g_assert_nonnull(silo); xb_silo_set_enable_node_cache(silo, TRUE); /* get node */ n = xb_silo_query_first(silo, "components/component", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_attr(n, "type"), ==, "desktop"); g_assert_cmpint(xb_node_get_depth(n), ==, 1); /* export a child */ xml2 = xb_node_query_export(n, "id", &error); g_assert_cmpstr(xml2, ==, "<id>gimp.desktop</id>"); /* get sibling */ c = xb_node_get_next(n); g_assert_nonnull(c); g_assert_cmpstr(xb_node_get_attr(c, "type"), ==, "firmware"); p = xb_node_get_next(c); g_assert_null(p); g_clear_object(&c); /* use the node to go back up the tree */ c = xb_node_query_first(n, "..", &error); g_assert_no_error(error); g_assert_nonnull(c); g_assert_cmpstr(xb_node_get_attr(c, "origin"), ==, "lvfs"); /* verify this is the parent */ p = xb_node_get_root(n); g_assert_cmpint(xb_node_get_depth(p), ==, 0); g_assert(c == p); children = xb_node_get_children(p); g_assert_nonnull(children); g_assert_cmpint(children->len, ==, 2); } static void xb_xpath_helpers_func(void) { const gchar *tmp; guint64 val; g_autoptr(GError) error = NULL; g_autoptr(XbNode) n = NULL; g_autoptr(XbSilo) silo = NULL; /* import from XML */ silo = xb_silo_new_from_xml("<release><checksum size=\"123\">456</checksum></release>", &error); g_assert_no_error(error); g_assert_nonnull(silo); /* as char */ n = xb_silo_get_root(silo); g_assert_nonnull(n); tmp = xb_node_query_text(n, "checksum", &error); g_assert_no_error(error); g_assert_cmpstr(tmp, ==, "456"); tmp = xb_node_query_attr(n, "checksum", "size", &error); g_assert_no_error(error); g_assert_cmpstr(tmp, ==, "123"); /* as uint64 */ val = xb_node_query_text_as_uint(n, "checksum", &error); g_assert_no_error(error); g_assert_cmpint(val, ==, 456); val = xb_node_query_attr_as_uint(n, "checksum", "size", &error); g_assert_no_error(error); g_assert_cmpint(val, ==, 123); } static void xb_xpath_query_func(void) { g_autoptr(GError) error = NULL; g_autoptr(XbNode) n = NULL; g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<components>\n" " <component timestamp=\"1631923200\">\n" " <id>n/a</id>\n" " </component>\n" "</components>\n"; /* import from XML */ silo = xb_silo_new_from_xml(xml, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* query with slash */ n = xb_silo_query_first(silo, "components/component/id[text()='n\\/a']", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "n/a"); g_clear_object(&n); /* query with an OR, where the first section contains an unknown element */ n = xb_silo_query_first(silo, "components/dave|components/component/id", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "n/a"); g_clear_object(&n); /* query with an OR, where the last section contains an unknown element */ n = xb_silo_query_first(silo, "components/component/id|components/dave", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "n/a"); g_clear_object(&n); /* query using an integer */ n = xb_silo_query_first(silo, "components/component[@timestamp=1631923200]", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_attr(n, "timestamp"), ==, "1631923200"); g_clear_object(&n); /* query with an OR, all sections contains an unknown element */ n = xb_silo_query_first(silo, "components/dave|components/mike", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); g_assert_null(n); } static void xb_xpath_incomplete_func(void) { g_autoptr(GError) error = NULL; g_autoptr(XbNode) n = NULL; g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<components>\n" " <component>\n" " <id>gimp.desktop</id>\n" " </component>\n" "</components>\n"; /* import from XML */ silo = xb_silo_new_from_xml(xml, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* query with MISSING '[' */ n = xb_silo_query_first(silo, "components/component/id[text()='dave'", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_null(n); } static gboolean xb_builder_fixup_tokenize_cb(XbBuilderFixup *self, XbBuilderNode *bn, gpointer user_data, GError **error) { if (g_strcmp0(xb_builder_node_get_element(bn), "name") == 0) xb_builder_node_tokenize_text(bn); return TRUE; } static gboolean xb_builder_fixup_strip_inner_cb(XbBuilderFixup *self, XbBuilderNode *bn, gpointer user_data, GError **error) { if (xb_builder_node_get_first_child(bn) == NULL) xb_builder_node_add_flag(bn, XB_BUILDER_NODE_FLAG_STRIP_TEXT); return TRUE; } static void xb_xpath_func(void) { XbNode *n; XbNode *n2; gboolean ret; g_autofree gchar *str = NULL; g_autofree gchar *xml_sub1 = NULL; g_autofree gchar *xml_sub2 = NULL; g_autofree gchar *xml_sub3 = NULL; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) results = NULL; g_autoptr(XbNode) n3 = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderFixup) fixup = NULL; g_autoptr(XbBuilderFixup) fixup2 = NULL; g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<components origin=\"lvfs\">\n" " <header>\n" " <csum type=\"sha1\">dead</csum>\n" " </header>\n" " <component type=\"desktop\">\n" " <id> gimp.desktop </id>\n" " <id>org.gnome.Gimp.desktop</id>\n" " <name>Mêẞ</name>\n" " <custom>\n" " <value key=\"KEY\">TRUE</value>\n" " </custom>\n" " </component>\n" " <component type=\"firmware\">\n" " <id>org.hughski.ColorHug2.firmware</id>\n" " </component>\n" "</components>\n"; /* tokenize specific fields */ fixup = xb_builder_fixup_new("TextTokenize", xb_builder_fixup_tokenize_cb, NULL, NULL); xb_builder_source_add_fixup(source, fixup); /* strip inner nodes without children */ fixup2 = xb_builder_fixup_new("TextStripInner", xb_builder_fixup_strip_inner_cb, NULL, NULL); xb_builder_source_add_fixup(source, fixup2); /* import from XML */ ret = xb_builder_source_load_xml(source, xml, XB_BUILDER_SOURCE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_import_source(builder, source); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* set up debugging */ xb_machine_set_debug_flags(xb_silo_get_machine(silo), XB_MACHINE_DEBUG_FLAG_SHOW_STACK); /* dump to screen */ str = xb_silo_to_string(silo, &error); g_assert_no_error(error); g_assert_nonnull(str); g_debug("\n%s", str); /* query with predicate 'in' */ xb_silo_set_profile_flags(silo, XB_SILO_PROFILE_FLAG_OPTIMIZER); n = xb_silo_query_first( silo, "components/component/id[text()=('gimp.desktop','another.desktop','dave.desktop')]", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "gimp.desktop"); g_clear_object(&n); /* query with predicate 'in' -- single-entry tuple */ xb_silo_set_profile_flags(silo, XB_SILO_PROFILE_FLAG_OPTIMIZER); n = xb_silo_query_first(silo, "components/component/id[text()=('gimp.desktop')]", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "gimp.desktop"); g_clear_object(&n); /* query with predicate logical and */ n = xb_silo_query_first( silo, "components/component/custom/value[(@key='KEY') and (text()='TRUE')]/../../id", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "gimp.desktop"); g_clear_object(&n); /* query with predicate logical and; failure */ n = xb_silo_query_first( silo, "components/component/custom/value[(@key='KEY')&&(text()='FALSE')]/../../id", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); g_assert_null(n); g_clear_error(&error); g_clear_object(&n); /* query with predicate logical and, alternate form */ n = xb_silo_query_first( silo, "components/component/custom/value[and((@key='KEY'),(text()='TRUE'))]/../../id", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "gimp.desktop"); g_clear_object(&n); /* query that doesn't find anything */ n = xb_silo_query_first(silo, "dave", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); g_assert_null(n); g_clear_error(&error); g_clear_object(&n); n = xb_silo_query_first(silo, "dave/dave", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); g_assert_null(n); g_clear_error(&error); g_clear_object(&n); n = xb_silo_query_first(silo, "components/dave", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); g_assert_null(n); g_clear_error(&error); g_clear_object(&n); n = xb_silo_query_first(silo, "components/component[@type='dave']/id", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); g_assert_null(n); g_clear_error(&error); g_clear_object(&n); n = xb_silo_query_first(silo, "components/component[@percentage>=90]", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); g_assert_null(n); g_clear_error(&error); g_clear_object(&n); n = xb_silo_query_first(silo, "components/component/id[text()='dave']", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); g_assert_null(n); g_clear_error(&error); g_clear_object(&n); /* query with attr opcodes */ n = xb_silo_query_first(silo, "components/component[@type='firmware']/id", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "org.hughski.ColorHug2.firmware"); g_clear_object(&n); /* query with attr opcodes */ n = xb_silo_query_first(silo, "components/component[@type!='firmware']/id", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "gimp.desktop"); g_clear_object(&n); /* query with attr opcodes with quotes */ n = xb_silo_query_first(silo, "components/component[@type='firmware']/id", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "org.hughski.ColorHug2.firmware"); g_clear_object(&n); /* query with position */ n = xb_silo_query_first(silo, "components/component[2]/id", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "org.hughski.ColorHug2.firmware"); g_clear_object(&n); /* last() with position */ n = xb_silo_query_first(silo, "components/component[last()]/id", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "org.hughski.ColorHug2.firmware"); g_clear_object(&n); /* query with attr opcodes that exists */ n = xb_silo_query_first(silo, "components/component[@type]/id", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "gimp.desktop"); g_clear_object(&n); /* query with attrs that do not exist */ n = xb_silo_query_first(silo, "components/component[not(@dave)]/id", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "gimp.desktop"); g_clear_object(&n); /* query with wildcard with predicate */ n = xb_silo_query_first(silo, "components/*[@type]/id", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "gimp.desktop"); g_clear_object(&n); /* query with text opcodes */ n = xb_silo_query_first(silo, "components/header/csum[text()='dead']", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_attr(n, "type"), ==, "sha1"); g_clear_object(&n); /* query with search */ n = xb_silo_query_first(silo, "components/component/id[text()~='gimp']", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "gimp.desktop"); g_clear_object(&n); /* query no normalize */ n = xb_silo_query_first(silo, "components/component/name", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "Mêẞ"); g_clear_object(&n); /* query name not UTF-8 */ n = xb_silo_query_first(silo, "components/component/name[text()~='mEss']/../id", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "gimp.desktop"); g_clear_object(&n); /* query with stem */ #ifdef HAVE_LIBSTEMMER xb_machine_set_debug_flags(xb_silo_get_machine(silo), XB_MACHINE_DEBUG_FLAG_SHOW_STACK | XB_MACHINE_DEBUG_FLAG_SHOW_PARSING); n = xb_silo_query_first(silo, "components/component/id[text()~=stem('gimping')]", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "gimp.desktop"); g_clear_object(&n); xb_machine_set_debug_flags(xb_silo_get_machine(silo), XB_MACHINE_DEBUG_FLAG_SHOW_STACK); #endif /* query with text:integer */ n = xb_silo_query_first(silo, "components/component/id['123'=123]", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "gimp.desktop"); g_clear_object(&n); /* query with integer:text */ n = xb_silo_query_first(silo, "components/component/id[123='123']", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "gimp.desktop"); g_clear_object(&n); /* query with prefix */ n = xb_silo_query_first(silo, "components/component/id[starts-with(text(),'gimp')]", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "gimp.desktop"); g_clear_object(&n); /* query with suffix */ n = xb_silo_query_first(silo, "components/component/id[ends-with(text(),'.desktop')]", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "gimp.desktop"); g_clear_object(&n); /* query with contains */ n = xb_silo_query_first(silo, "components/component/id[contains(text(),'imp')]", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "gimp.desktop"); g_clear_object(&n); /* query with type-conversion */ n = xb_silo_query_first(silo, "components/component[position()=number('2')]/id", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "org.hughski.ColorHug2.firmware"); g_clear_object(&n); /* query with another type-conversion */ n = xb_silo_query_first(silo, "components/component['2'=string(2)]/id", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "gimp.desktop"); g_clear_object(&n); /* query with backtrack */ g_debug("\n%s", xml); n = xb_silo_query_first( silo, "components/component[@type='firmware']/id[text()='org.hughski.ColorHug2.firmware']", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "org.hughski.ColorHug2.firmware"); g_clear_object(&n); /* query with nesting */ g_debug("\n%s", xml); n = xb_silo_query_first( silo, "components/component/id[text()=lower-case(upper-case('Gimp.DESKTOP'))]", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "gimp.desktop"); g_clear_object(&n); /* query for multiple results */ results = xb_silo_query(silo, "components/component/id", 5, &error); g_assert_no_error(error); g_assert_nonnull(results); g_assert_cmpint(results->len, ==, 3); n2 = g_ptr_array_index(results, 2); g_assert_cmpstr(xb_node_get_text(n2), ==, "org.hughski.ColorHug2.firmware"); /* subtree export */ xml_sub1 = xb_node_export(n2, XB_NODE_EXPORT_FLAG_NONE, &error); g_assert_no_error(error); g_assert_nonnull(xml_sub1); g_assert_cmpstr(xml_sub1, ==, "<id>org.hughski.ColorHug2.firmware</id>"); /* parent of subtree */ n3 = xb_node_get_parent(n2); g_assert(n3 != NULL); xml_sub2 = xb_node_export(n3, XB_NODE_EXPORT_FLAG_NONE, &error); g_assert_no_error(error); g_assert_nonnull(xml_sub2); g_assert_cmpstr( xml_sub2, ==, "<component type=\"firmware\"><id>org.hughski.ColorHug2.firmware</id></component>"); /* only children of parent */ xml_sub3 = xb_node_export(n3, XB_NODE_EXPORT_FLAG_ONLY_CHILDREN, &error); g_assert_no_error(error); g_assert_nonnull(xml_sub3); g_assert_cmpstr(xml_sub3, ==, "<id>org.hughski.ColorHug2.firmware</id>"); } static void xb_xpath_null_attr_func(void) { XbNode *n; gboolean ret; g_autoptr(GError) error = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<component type=\"desktop\">\n" " <id>org.gnome.Software</id>\n" "</component>\n"; ret = xb_builder_source_load_xml(source, xml, XB_BUILDER_SOURCE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_import_source(builder, source); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* lower-case with nonexistent attr should not crash */ n = xb_silo_query_first(silo, "component[lower-case(attr('nonexistent'))='x']", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); g_assert_null(n); g_clear_error(&error); /* upper-case with nonexistent attr should not crash */ n = xb_silo_query_first(silo, "component[upper-case(attr('nonexistent'))='x']", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); g_assert_null(n); g_clear_error(&error); /* starts-with with nonexistent attr should not crash */ n = xb_silo_query_first(silo, "component[starts-with(attr('nonexistent'),'x')]", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); g_assert_null(n); g_clear_error(&error); /* ends-with with nonexistent attr should not crash */ n = xb_silo_query_first(silo, "component[ends-with(attr('nonexistent'),'x')]", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); g_assert_null(n); g_clear_error(&error); #ifdef HAVE_LIBSTEMMER /* stem with nonexistent attr should not crash */ n = xb_silo_query_first(silo, "component[stem(attr('nonexistent'))='x']", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); g_assert_null(n); g_clear_error(&error); #endif } static void xb_xpath_in_func(void) { XbNode *n; gboolean ret; g_autoptr(GError) error = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<component><id>test</id></component>\n"; ret = xb_builder_source_load_xml(source, xml, XB_BUILDER_SOURCE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_import_source(builder, source); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* in() with valid match should work */ n = xb_silo_query_first(silo, "component/id[text()=('test','other')]", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "test"); g_clear_object(&n); /* in() with no match should return NOT_FOUND, not crash */ n = xb_silo_query_first(silo, "component/id[text()=('nope','nada')]", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); g_assert_null(n); g_clear_error(&error); } static void xb_xpath_in_lower_func(void) { XbNode *n; gboolean ret; g_autoptr(GError) error = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<component><id>test</id></component>\n"; ret = xb_builder_source_load_xml(source, xml, XB_BUILDER_SOURCE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_import_source(builder, source); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* in() with lower-case() should match */ n = xb_silo_query_first(silo, "component/id[in(text(),lower-case('TEST'))]", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "test"); g_clear_object(&n); /* in() with lower-case() no match */ n = xb_silo_query_first(silo, "component/id[in(text(),lower-case('NOPE'))]", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); g_assert_null(n); g_clear_error(&error); } static void xb_xpath_in_null_needle_func(void) { XbNode *n; gboolean ret; g_autoptr(GError) error = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<component><id>test</id></component>\n"; ret = xb_builder_source_load_xml(source, xml, XB_BUILDER_SOURCE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_import_source(builder, source); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* in() with a missing attribute (NULL needle) should not crash */ n = xb_silo_query_first(silo, "component/id[attr('missing')=('a','b')]", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); g_assert_null(n); g_clear_error(&error); /* in() with a NULL haystack entry (from attr on missing attribute) should not crash */ n = xb_silo_query_first(silo, "component/id[in(text(),lower-case(attr('missing')))]", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); g_assert_null(n); g_clear_error(&error); } static void xb_xpath_stem_func(void) { XbNode *n; gboolean ret; g_autoptr(GError) error = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<component type=\"desktop\">\n" " <id>test</id>\n" "</component>\n"; ret = xb_builder_source_load_xml(source, xml, XB_BUILDER_SOURCE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_import_source(builder, source); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* stem should type-check the top of stack, not the bottom */ n = xb_silo_query_first(silo, "component/id[stem(text())='test']", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "test"); g_clear_object(&n); /* stem with no match */ n = xb_silo_query_first(silo, "component/id[stem(text())='nope']", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); g_assert_null(n); g_clear_error(&error); } static void xb_xpath_or_limit_func(void) { gboolean ret; g_autoptr(GError) error = NULL; g_autoptr(GString) xpath = g_string_new(NULL); g_autoptr(GPtrArray) results = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<component><id>test</id></component>\n"; ret = xb_builder_source_load_xml(source, xml, XB_BUILDER_SOURCE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_import_source(builder, source); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* build a query with too many OR branches */ for (guint i = 0; i < 100; i++) { if (i > 0) g_string_append_c(xpath, '|'); g_string_append(xpath, "component/id"); } results = xb_silo_query(silo, xpath->str, 0, &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA); g_assert_null(results); g_clear_error(&error); } static void xb_xpath_operator_depth_limit_func(void) { XbNode *n; gboolean ret; g_autoptr(GError) error = NULL; g_autoptr(GString) xpath = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<component><id>test</id></component>\n"; ret = xb_builder_source_load_xml(source, xml, XB_BUILDER_SOURCE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_import_source(builder, source); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* a predicate with many chained operators should be rejected, not crash */ xpath = g_string_new("component/id["); for (guint i = 0; i < 25; i++) { if (i > 0) g_string_append_c(xpath, '='); g_string_append(xpath, "'x'"); } g_string_append_c(xpath, ']'); n = xb_silo_query_first(silo, xpath->str, &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_NO_SPACE); g_assert_null(n); g_clear_error(&error); /* a predicate within the limit should still work */ n = xb_silo_query_first(silo, "component/id[text()='test']", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "test"); g_clear_object(&n); } static void xb_xpath_section_limit_func(void) { gboolean ret; g_autoptr(GError) error = NULL; g_autoptr(GString) xpath = g_string_new(NULL); g_autoptr(GPtrArray) results = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<component><id>test</id></component>\n"; ret = xb_builder_source_load_xml(source, xml, XB_BUILDER_SOURCE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_import_source(builder, source); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* build a query with too many sections (>100) */ for (guint i = 0; i < 102; i++) { if (i > 0) g_string_append_c(xpath, '/'); g_string_append(xpath, "a"); } results = xb_silo_query(silo, xpath->str, 0, &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA); g_assert_null(results); g_clear_error(&error); } static void xb_builder_strtab_ntags_limit_func(void) { g_autoptr(GError) error = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderNode) root = xb_builder_node_new("root"); g_autoptr(XbSilo) silo = NULL; /* build a node tree with more than 65535 unique element names */ for (guint i = 0; i <= G_MAXUINT16; i++) { g_autofree gchar *name = g_strdup_printf("e%u", i); xb_builder_node_insert_text(root, name, NULL, NULL); } xb_builder_import_node(builder, root); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA); g_assert_null(silo); } static void xb_xpath_predicate_limit_func(void) { XbNode *n; gboolean ret; g_autoptr(GError) error = NULL; g_autoptr(GString) xpath = g_string_new("component/id"); g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<component><id>test</id></component>\n"; ret = xb_builder_source_load_xml(source, xml, XB_BUILDER_SOURCE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_import_source(builder, source); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* build a query with too many predicates (>20) */ for (guint i = 0; i < 21; i++) g_string_append(xpath, "[text()='test']"); n = xb_silo_query_first(silo, xpath->str, &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA); g_assert_null(n); g_clear_error(&error); } static void xb_manual_token_search_func(void) { XbNode *n; g_autoptr(XbNode) cpt_node = NULL; g_autoptr(XbBuilder) builder = NULL; g_autoptr(XbBuilderNode) bn_root = NULL; g_autoptr(XbBuilderNode) bn = NULL; g_autoptr(XbSilo) silo = NULL; g_autofree gchar *str = NULL; g_autoptr(XbQuery) query = NULL; g_autoptr(GPtrArray) result = NULL; g_autoptr(GError) error = NULL; g_auto(XbQueryContext) context = XB_QUERY_CONTEXT_INIT(); /* create node tree and add a dummy token manually */ bn_root = xb_builder_node_new("component"); bn = xb_builder_node_new("summary"); xb_builder_node_set_text(bn, "A strategy game", -1); xb_builder_node_add_token(bn, "strategy"); /* or use XB_BUILDER_NODE_FLAG_TOKENIZE_TEXT */ xb_builder_node_add_token(bn, "strategi"); xb_builder_node_add_child(bn_root, bn); builder = xb_builder_new(); xb_builder_import_node(builder, bn_root); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* dump to screen */ str = xb_silo_to_string(silo, &error); g_assert_no_error(error); g_assert_nonnull(str); g_debug("\n%s", str); /* get component node */ cpt_node = xb_silo_query_first(silo, "component", &error); g_assert_no_error(error); g_assert_nonnull(cpt_node); /* search using the slow, token-less path */ query = xb_query_new(silo, "summary[text()~=?]", &error); g_assert_no_error(error); g_assert_nonnull(query); xb_value_bindings_bind_str(xb_query_context_get_bindings(&context), 0, "strategy", NULL); result = xb_node_query_with_context(cpt_node, query, &context, &error); g_assert_no_error(error); g_assert_nonnull(result); n = XB_NODE(g_ptr_array_index(result, 0)); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "A strategy game"); g_clear_object(&query); g_ptr_array_unref(g_steal_pointer(&result)); /* search for token */ xb_silo_set_profile_flags(silo, ~0); query = xb_query_new(silo, "summary[text()~=?]", &error); g_assert_no_error(error); g_assert_nonnull(query); xb_value_bindings_bind_str(xb_query_context_get_bindings(&context), 0, "strategi", NULL); result = xb_node_query_with_context(cpt_node, query, &context, &error); g_assert_no_error(error); g_assert_nonnull(result); n = XB_NODE(g_ptr_array_index(result, 0)); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "A strategy game"); } static void xb_builder_native_lang_func(void) { gboolean ret; g_autoptr(GError) error = NULL; g_autofree gchar *str = NULL; g_autofree gchar *tmp = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbNode) n = NULL; g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<components>\n" " <component>\n" " <p xml:lang=\"de_DE\">Wilcommen</p>\n" " <p>Hello</p>\n" " <p xml:lang=\"fr\">Salut</p>\n" " <p>Goodbye</p>\n" " <p xml:lang=\"de_DE\">Auf Wiedersehen</p>\n" " <p xml:lang=\"fr\">Au revoir</p>\n" " </component>\n" "</components>\n"; /* import from XML */ ret = xb_test_import_xml(builder, xml, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_add_locale(builder, "fr_FR.UTF-8"); xb_builder_add_locale(builder, "fr_FR"); xb_builder_add_locale(builder, "fr_FR"); xb_builder_add_locale(builder, "fr"); xb_builder_add_locale(builder, "C"); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_SINGLE_LANG, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* test we removed other languages */ str = xb_silo_to_string(silo, &error); g_assert_no_error(error); g_assert_nonnull(str); g_debug("\n%s", str); g_assert_null(g_strstr_len(str, -1, "Wilcommen")); g_assert_null(g_strstr_len(str, -1, "Hello")); g_assert_nonnull(g_strstr_len(str, -1, "Salut")); g_assert_null(g_strstr_len(str, -1, "Goodbye")); g_assert_null(g_strstr_len(str, -1, "Auf Wiedersehen")); g_assert_nonnull(g_strstr_len(str, -1, "Au revoir")); /* test we traversed the tree correctly */ n = xb_silo_query_first(silo, "components/component/*", &error); g_assert_no_error(error); g_assert_nonnull(n); tmp = xb_node_export(n, XB_NODE_EXPORT_FLAG_INCLUDE_SIBLINGS, &error); g_assert_cmpstr(tmp, ==, "<p xml:lang=\"fr\">Salut</p><p xml:lang=\"fr\">Au revoir</p>"); } static void xb_builder_comments_func(void) { gboolean ret; g_autoptr(GError) error = NULL; g_autofree gchar *str = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<?xml version=\"1.0\" ?>\n" "<components>\n" " <!-- one -->\n" " <!-- two -->\n" "</components>\n"; /* import XML */ ret = xb_test_import_xml(builder, xml, &error); g_assert_no_error(error); g_assert_true(ret); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* export */ str = xb_silo_export(silo, XB_NODE_EXPORT_FLAG_NONE, &error); g_assert_no_error(error); g_assert_cmpstr(str, ==, "<components></components>"); } static void xb_builder_cdata_func(void) { gboolean ret; g_autoptr(GError) error = NULL; g_autofree gchar *str = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<?xml version=\"1.0\" ?>\n" "<components>\n" " <component>\n" " <text>plain text</text>\n" " <summary><![CDATA[1 2 3]]></summary>\n" " <description><p>s <![CDATA[1]]> p <![CDATA[2]]> l <![CDATA[3]]> i <![CDATA[4]]> " "t</p><p><![CDATA[><]]></p><p><![CDATA[\"]]></p></description>\n" " </component>\n" "</components>\n"; /* import XML */ ret = xb_test_import_xml(builder, xml, &error); g_assert_no_error(error); g_assert_true(ret); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* export */ str = xb_silo_export(silo, XB_NODE_EXPORT_FLAG_NONE, &error); g_assert_no_error(error); g_assert_cmpstr(str, ==, "<components><component><text>plain text</text><summary>1 2 " "3</summary><description><p>s 1 p 2 l 3 i 4 " "t</p><p>><</p><p>"</p></description></component></components>"); } static void xb_builder_native_lang2_func(void) { gboolean ret; g_autoptr(GError) error = NULL; g_autofree gchar *str = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<components>\n" " <component>\n" " <description xml:lang=\"de_DE\"><p>Wilcommen</p></description>\n" " <description><p>Hello</p></description>\n" " <description xml:lang=\"fr\"><p>Salut</p></description>\n" " </component>\n" "</components>\n"; /* import from XML */ ret = xb_test_import_xml(builder, xml, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_add_locale(builder, "fr_FR"); xb_builder_add_locale(builder, "fr"); xb_builder_add_locale(builder, "C"); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_SINGLE_LANG, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* test we removed other languages */ str = xb_silo_to_string(silo, &error); g_assert_no_error(error); g_assert_nonnull(str); g_assert_null(g_strstr_len(str, -1, "Wilcommen")); g_assert_null(g_strstr_len(str, -1, "Hello")); g_assert_nonnull(g_strstr_len(str, -1, "Salut")); g_debug("\n%s", str); } static void xb_xpath_parent_func(void) { XbNode *n; gboolean ret; g_autoptr(GError) error = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<components origin=\"lvfs\">\n" " <header>\n" " <csum type=\"sha1\">dead</csum>\n" " </header>\n" " <component type=\"desktop\">\n" " <id>gimp.desktop</id>\n" " <id>org.gnome.Gimp.desktop</id>\n" " </component>\n" " <component type=\"firmware\">\n" " <id>org.hughski.ColorHug2.firmware</id>\n" " <pkgname>colorhug-client</pkgname>\n" " <description xml:lang=\"de_DE\"><p>Wilcommen!</p></description>\n" " <description><p>hello!</p></description>\n" " <description xml:lang=\"fr_FR\"><p>Bonjour!</p></description>\n" " <project_license>GPL-2.0</project_license>\n" " </component>\n" "</components>\n"; /* import from XML */ ret = xb_test_import_xml(builder, xml, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_add_locale(builder, "C"); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NATIVE_LANGS, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* get node, no parent */ n = xb_silo_query_first(silo, "components/component[@type='firmware']/id", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "org.hughski.ColorHug2.firmware"); g_assert_cmpstr(xb_node_get_element(n), ==, "id"); g_clear_object(&n); /* get node, one parent */ n = xb_silo_query_first(silo, "components/component[@type='firmware']/id/..", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_element(n), ==, "component"); g_clear_object(&n); /* get node, multiple parents */ n = xb_silo_query_first(silo, "components/component[@type='firmware']/id/../..", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_element(n), ==, "components"); g_clear_object(&n); /* descend, ascend, descend */ n = xb_silo_query_first(silo, "components/component[@type='firmware']/pkgname/../project_license", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "GPL-2.0"); g_clear_object(&n); /* descend, ascend, descend */ n = xb_silo_query_first(silo, "components/component/pkgname[text()~='colorhug']/../id", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "org.hughski.ColorHug2.firmware"); g_clear_object(&n); /* get node, too many parents */ n = xb_silo_query_first(silo, "components/component[@type='firmware']/id/../../..", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_null(n); g_clear_error(&error); /* can't go lower than root */ n = xb_silo_query_first(silo, "..", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_null(n); g_clear_error(&error); /* fuzzy substring match */ n = xb_silo_query_first(silo, "components/component/pkgname[text()~='colorhug']", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "colorhug-client"); g_clear_object(&n); /* strlen match */ n = xb_silo_query_first(silo, "components/component/pkgname[string-length(text())==15]", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "colorhug-client"); g_clear_object(&n); /* fuzzy substring match */ n = xb_silo_query_first(silo, "components/component[@type~='firm']/pkgname", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "colorhug-client"); g_clear_object(&n); } static void xb_xpath_prepared_func(void) { XbNode *n; gboolean ret; g_autoptr(GError) error = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbSilo) silo = NULL; g_autoptr(XbQuery) query = NULL; g_auto(XbQueryContext) context = XB_QUERY_CONTEXT_INIT(); g_autoptr(XbNode) component = NULL; g_autoptr(GPtrArray) components = NULL; const gchar *xml = "<components origin=\"lvfs\">\n" " <component type=\"desktop\">\n" " <id>gimp.desktop</id>\n" " <id>org.gnome.Gimp.desktop</id>\n" " </component>\n" " <component type=\"firmware\">\n" " <id>org.hughski.ColorHug2.firmware</id>\n" " <pkgname>colorhug-client</pkgname>\n" " </component>\n" "</components>\n"; /* import from XML */ ret = xb_test_import_xml(builder, xml, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_add_locale(builder, "C"); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NATIVE_LANGS, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* get first component */ component = xb_silo_query_first(silo, "components/component", &error); g_assert_no_error(error); g_assert_nonnull(component); /* prepared statement on node */ query = xb_query_new(silo, "id[text()=?]/..", &error); g_assert_no_error(error); g_assert_nonnull(query); xb_value_bindings_bind_str(xb_query_context_get_bindings(&context), 0, "gimp.desktop", NULL); components = xb_node_query_with_context(component, query, &context, &error); g_assert_no_error(error); g_assert_nonnull(components); g_assert_cmpint(components->len, ==, 1); n = g_ptr_array_index(components, 0); g_assert_cmpstr(xb_node_get_attr(n, "type"), ==, "desktop"); } static void xb_xpath_query_reverse_func(void) { XbNode *n; gboolean ret; g_autoptr(GError) error = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbSilo) silo = NULL; g_autoptr(XbQuery) query = NULL; g_autoptr(GPtrArray) names = NULL; const gchar *xml = "<names>\n" " <name>foo</name>\n" " <name>bar</name>\n" " <name>baz</name>\n" "</names>\n"; /* import from XML */ ret = xb_test_import_xml(builder, xml, &error); g_assert_no_error(error); g_assert_true(ret); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* get first when reversed */ query = xb_query_new_full(silo, "names/name", XB_QUERY_FLAG_REVERSE, &error); g_assert_no_error(error); g_assert_nonnull(query); names = xb_silo_query_with_context(silo, query, NULL, &error); g_assert_no_error(error); g_assert_nonnull(names); g_assert_cmpint(names->len, ==, 3); n = g_ptr_array_index(names, 0); g_assert_cmpstr(xb_node_get_text(n), ==, "baz"); } static void xb_xpath_query_force_node_cache_func(void) { gboolean ret; g_autoptr(GError) error = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbNode) n1 = NULL; g_autoptr(XbNode) n2 = NULL; g_autoptr(XbQuery) query = NULL; g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<names>\n" " <name>foo</name>\n" "</names>\n"; /* import from XML */ ret = xb_test_import_xml(builder, xml, &error); g_assert_no_error(error); g_assert_true(ret); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* use a cache for this specific result */ query = xb_query_new_full(silo, "names/name", XB_QUERY_FLAG_FORCE_NODE_CACHE, &error); g_assert_no_error(error); g_assert_nonnull(query); n1 = xb_silo_query_first_with_context(silo, query, NULL, &error); g_assert_no_error(error); g_assert_nonnull(n1); n2 = xb_silo_query_first_with_context(silo, query, NULL, &error); g_assert_no_error(error); g_assert_nonnull(n2); g_assert(n1 == n2); } static void xb_xpath_glob_func(void) { g_autofree gchar *xml2 = NULL; g_autoptr(GError) error = NULL; g_autoptr(XbNode) n = NULL; g_autoptr(XbSilo) silo = NULL; const gchar *xml = "<components origin=\"lvfs\">\n" " <component type=\"desktop\">\n" " <id>gimp.desktop</id>\n" " <id>org.gnome.GIMP.desktop</id>\n" " </component>\n" "</components>\n"; /* import from XML */ silo = xb_silo_new_from_xml(xml, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* get node, no parent */ n = xb_silo_query_first(silo, "components/component[@type='desktop']/*", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_element(n), ==, "id"); /* export this one node */ xml2 = xb_node_export(n, XB_NODE_EXPORT_FLAG_NONE, &error); g_assert_no_error(error); g_assert_cmpstr(xml2, ==, "<id>gimp.desktop</id>"); } static void xb_builder_multiple_roots_func(void) { gboolean ret; g_autofree gchar *str = NULL; g_autofree gchar *xml_new = NULL; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) results = NULL; g_autoptr(XbBuilder) builder = NULL; g_autoptr(XbSilo) silo = NULL; /* import from XML */ builder = xb_builder_new(); ret = xb_test_import_xml(builder, "<tag>value</tag>", &error); g_assert_no_error(error); g_assert_true(ret); ret = xb_test_import_xml(builder, "<tag>value2</tag><tag>value3</tag>", &error); g_assert_no_error(error); g_assert_true(ret); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* convert back to XML */ str = xb_silo_to_string(silo, &error); g_assert_no_error(error); g_assert_nonnull(str); g_debug("\n%s", str); xml_new = xb_silo_export(silo, XB_NODE_EXPORT_FLAG_INCLUDE_SIBLINGS, &error); g_assert_no_error(error); g_assert_nonnull(xml_new); g_print("%s", xml_new); g_assert_cmpstr("<tag>value</tag><tag>value2</tag><tag>value3</tag>", ==, xml_new); /* query for multiple results */ results = xb_silo_query(silo, "tag", 5, &error); g_assert_no_error(error); g_assert_nonnull(results); g_assert_cmpint(results->len, ==, 3); } static void xb_builder_single_root_func(void) { gboolean ret; g_autoptr(GError) error = NULL; g_autoptr(XbBuilder) builder = NULL; g_autoptr(XbSilo) silo = NULL; /* import from XML */ builder = xb_builder_new(); ret = xb_test_import_xml(builder, "<tag>value2</tag><tag>value3</tag>", &error); g_assert_no_error(error); g_assert_true(ret); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_SINGLE_ROOT, NULL, &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA); g_assert_null(silo); } static void xb_builder_node_token_max_func(void) { g_autofree gchar *xml = NULL; g_autoptr(GError) error = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderNode) components = NULL; g_autoptr(XbBuilderNode) root = xb_builder_node_new(NULL); g_autoptr(XbSilo) silo = NULL; /* create a simple document */ components = xb_builder_node_insert(root, "components", NULL); for (guint i = 0; i < XB_OPCODE_TOKEN_MAX * 2; i++) { g_autofree gchar *tmp = g_strdup_printf("foobarbaz%04u", i); xb_builder_node_add_token(components, tmp); } /* import the doc */ xb_builder_import_node(builder, root); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* check the XML */ xml = xb_silo_export(silo, XB_NODE_EXPORT_FLAG_COLLAPSE_EMPTY, &error); g_assert_no_error(error); g_assert_nonnull(xml); g_assert_cmpstr("<components />", ==, xml); } static void xb_builder_node_nul_func(void) { const gchar buf[] = {'A', '\n', 'B'}; g_autoptr(XbBuilderNode) root = xb_builder_node_new(NULL); xb_builder_node_set_text(root, "\n", 0); g_assert_cmpstr(xb_builder_node_get_text(root), ==, NULL); xb_builder_node_set_text(root, "dave", -1); g_assert_cmpstr(xb_builder_node_get_text(root), ==, "dave"); xb_builder_node_set_text(root, buf, sizeof(buf)); g_assert_cmpstr(xb_builder_node_get_text(root), ==, "A B"); } static void xb_builder_node_func(void) { g_autofree gchar *xml = NULL; g_autofree gchar *xml_src = NULL; g_autoptr(GError) error = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderNode) child_by_element = NULL; g_autoptr(XbBuilderNode) child_by_text = NULL; g_autoptr(XbBuilderNode) component = NULL; g_autoptr(XbBuilderNode) components = NULL; g_autoptr(XbBuilderNode) id = NULL; g_autoptr(XbBuilderNode) description = xb_builder_node_new("description"); g_autoptr(XbBuilderNode) em = xb_builder_node_new("em"); g_autoptr(XbBuilderNode) empty = NULL; g_autoptr(XbBuilderNode) root = xb_builder_node_new(NULL); g_autoptr(XbSilo) silo = NULL; /* create a simple document */ components = xb_builder_node_insert(root, "components", "origin", "lvfs", NULL); g_assert_cmpint(xb_builder_node_depth(components), ==, 1); component = xb_builder_node_insert(components, "component", NULL); g_assert_cmpint(xb_builder_node_depth(component), ==, 2); xb_builder_node_set_attr(component, "type", "firmware"); xb_builder_node_set_attr(component, "type", "desktop"); g_assert_cmpstr(xb_builder_node_get_attr(component, "type"), ==, "desktop"); g_assert_cmpstr(xb_builder_node_get_attr(component, "dave"), ==, NULL); id = xb_builder_node_new("id"); xb_builder_node_add_flag(id, XB_BUILDER_NODE_FLAG_TOKENIZE_TEXT); xb_builder_node_add_token(id, "foobarbaz"); xb_builder_node_add_child(component, id); xb_builder_node_set_text(id, "gimp.desktop", -1); xb_builder_node_insert_text(component, "icon", "dave", "type", "stock", NULL); g_assert_cmpint(xb_builder_node_depth(id), ==, 3); xb_builder_node_add_flag(em, XB_BUILDER_NODE_FLAG_LITERAL_TEXT); xb_builder_node_set_text(em, "world!", -1); xb_builder_node_set_tail(em, " ", -1); xb_builder_node_add_child(description, em); xb_builder_node_set_text(description, "hello ", -1); xb_builder_node_add_child(component, description); /* no text contents */ empty = xb_builder_node_insert(component, "empty", NULL); xb_builder_node_set_text(empty, NULL, -1); xb_builder_node_set_tail(empty, NULL, -1); /* get specific child */ child_by_element = xb_builder_node_get_child(components, "component", NULL); g_assert_nonnull(child_by_element); g_assert_cmpstr(xb_builder_node_get_element(child_by_element), ==, "component"); child_by_text = xb_builder_node_get_child(component, "id", "gimp.desktop"); g_assert_nonnull(child_by_text); g_assert_cmpstr(xb_builder_node_get_element(child_by_text), ==, "id"); /* check the source XML */ xml_src = xb_builder_node_export(components, XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE | XB_NODE_EXPORT_FLAG_COLLAPSE_EMPTY, &error); g_assert_no_error(error); g_assert_nonnull(xml_src); g_print("%s", xml_src); g_assert_cmpstr("<components origin=\"lvfs\">\n" "<component type=\"desktop\">\n" "<id>gimp.desktop</id>\n" "<icon type=\"stock\">dave</icon>\n" "<description>hello <em>world!</em> \n" "</description>\n" "<empty />\n" "</component>\n" "</components>\n", ==, xml_src); /* import the doc */ xb_builder_import_node(builder, root); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* check the XML */ xml = xb_silo_export(silo, XB_NODE_EXPORT_FLAG_INCLUDE_SIBLINGS | XB_NODE_EXPORT_FLAG_COLLAPSE_EMPTY, &error); g_assert_no_error(error); g_assert_nonnull(xml); g_print("%s", xml); g_assert_cmpstr("<components origin=\"lvfs\">" "<component type=\"desktop\">" "<id>gimp.desktop</id>" "<icon type=\"stock\">dave</icon>" "<description>hello <em>world!</em>" "</description>" "<empty />" "</component>" "</components>", ==, xml); } static void xb_builder_node_literal_text_func(void) { gboolean ret; g_autofree gchar *xml2 = NULL; g_autoptr(GError) error = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbSilo) silo = NULL; const gchar *xml = " <component>\n" " <description>\n" " <p>Really long content\n" "spanning multiple lines\n" "</p>\n" " </description>\n" " </component>\n"; /* import some XML */ ret = xb_builder_source_load_xml(source, xml, XB_BUILDER_SOURCE_FLAG_LITERAL_TEXT, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_import_source(builder, source); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* check the XML */ xml2 = xb_silo_export(silo, XB_NODE_EXPORT_FLAG_NONE, &error); g_assert_no_error(error); g_assert_nonnull(xml2); g_print("%s\n", xml2); g_assert_cmpstr( "<component>" "<description><p>Really long content\nspanning multiple lines\n</p></description>" "</component>", ==, xml2); } static void xb_builder_node_source_text_func(void) { gboolean ret; g_autofree gchar *xml2 = NULL; g_autoptr(GError) error = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbBuilderSource) source = xb_builder_source_new(); g_autoptr(XbSilo) silo = NULL; const gchar *xml = " <component>\n" " <description>\n" " <p>Really long content\n" "spanning multiple lines\n" "</p>\n" " </description>\n" " </component>\n"; /* import some XML */ ret = xb_builder_source_load_xml(source, xml, XB_BUILDER_SOURCE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_import_source(builder, source); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* check the XML */ xml2 = xb_silo_export(silo, XB_NODE_EXPORT_FLAG_NONE, &error); g_assert_no_error(error); g_assert_nonnull(xml2); g_print("%s\n", xml2); g_assert_cmpstr( "<component>" "<description><p>Really long content spanning multiple lines</p></description>" "</component>", ==, xml2); } static void xb_builder_node_info_func(void) { gboolean ret; g_autofree gchar *xml = NULL; g_autofree gchar *tmp_xml = g_build_filename(g_get_tmp_dir(), "temp.xml", NULL); g_autoptr(GError) error = NULL; g_autoptr(XbBuilderSource) import1 = xb_builder_source_new(); g_autoptr(XbBuilderSource) import2 = xb_builder_source_new(); g_autoptr(XbBuilder) builder = xb_builder_new(); g_autoptr(XbNode) n = NULL; g_autoptr(XbBuilderNode) info1 = NULL; g_autoptr(XbBuilderNode) info2 = NULL; g_autoptr(XbSilo) silo = NULL; g_autoptr(GFile) file = NULL; /* create a simple document with some info */ ret = g_file_set_contents(tmp_xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<component><id type=\"desktop\">dave</id></component>", -1, &error); g_assert_no_error(error); g_assert_true(ret); info1 = xb_builder_node_insert(NULL, "info", NULL); xb_builder_node_insert_text(info1, "scope", "user", NULL); info2 = xb_builder_node_insert(NULL, "info", NULL); xb_builder_node_insert_text(info2, "scope", "system", NULL); /* import the doc */ file = g_file_new_for_path(tmp_xml); ret = xb_builder_source_load_file(import1, file, XB_BUILDER_SOURCE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_source_set_info(import1, info1); xb_builder_source_set_prefix(import1, "local"); xb_builder_import_source(builder, import1); ret = xb_builder_source_load_file(import2, file, XB_BUILDER_SOURCE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_true(ret); xb_builder_source_set_info(import2, info2); xb_builder_source_set_prefix(import2, "local"); xb_builder_import_source(builder, import2); silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* get info */ n = xb_silo_query_first(silo, "local/component/id[text()='dave']/../info/scope", &error); g_assert_no_error(error); g_assert_nonnull(n); g_assert_cmpstr(xb_node_get_text(n), ==, "user"); /* check the XML */ xml = xb_silo_export(silo, XB_NODE_EXPORT_FLAG_INCLUDE_SIBLINGS, &error); g_assert_no_error(error); g_assert_nonnull(xml); g_assert_cmpstr("<local>" "<component>" "<id type=\"desktop\">dave</id>" "<info>" "<scope>user</scope>" "</info>" "</component>" "<component>" "<id type=\"desktop\">dave</id>" "<info>" "<scope>system</scope>" "</info>" "</component>" "</local>", ==, xml); } static void xb_threading_cb(gpointer data, gpointer user_data) { XbSilo *silo = XB_SILO(user_data); gint i = g_random_int_range(0, 50); g_autofree gchar *xpath = NULL; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) components = NULL; /* do query */ xpath = g_strdup_printf("components/component/id[text()='%06i.firmware']", i); components = xb_silo_query(silo, xpath, 0, &error); g_assert_no_error(error); g_assert_nonnull(components); g_assert_cmpint(components->len, ==, 1); g_print("."); } static void xb_threading_func(void) { GThreadPool *pool; gboolean ret; guint n_components = 10000; g_autoptr(GError) error = NULL; g_autoptr(GString) xml = g_string_new(NULL); g_autoptr(XbSilo) silo = NULL; #ifdef __s390x__ /* this is run with qemu and takes too much time */ g_test_skip("s390 too slow, skipping"); return; #endif /* create a huge document */ g_string_append(xml, "<components>"); for (guint i = 0; i < n_components; i++) { g_string_append(xml, "<component>"); g_string_append_printf(xml, " <id>%06u.firmware</id>", i); g_string_append(xml, " <name>ColorHug2</name>"); g_string_append(xml, " <summary>Firmware</summary>"); g_string_append(xml, " <description><p>New features!</p></description>"); g_string_append(xml, "</component>"); } g_string_append(xml, "</components>"); /* import from XML */ silo = xb_silo_new_from_xml(xml->str, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* create thread pool */ pool = g_thread_pool_new(xb_threading_cb, silo, 20, TRUE, &error); g_assert_no_error(error); g_assert_nonnull(pool); /* run threads */ for (guint i = 0; i < 100; i++) { ret = g_thread_pool_push(pool, &i, &error); g_assert_no_error(error); g_assert_true(ret); } g_thread_pool_free(pool, FALSE, TRUE); } typedef struct { guint cnt; GString *str; } XbMarkupHelper; static gboolean xb_markup_head_cb(XbNode *n, gpointer user_data) { XbMarkupHelper *helper = (XbMarkupHelper *)user_data; helper->cnt++; if (xb_node_get_text(n) == NULL) return FALSE; /* start */ if (g_strcmp0(xb_node_get_element(n), "em") == 0) { g_string_append(helper->str, "*"); } else if (g_strcmp0(xb_node_get_element(n), "strong") == 0) { g_string_append(helper->str, "**"); } else if (g_strcmp0(xb_node_get_element(n), "code") == 0) { g_string_append(helper->str, "`"); } /* text */ if (xb_node_get_text(n) != NULL) g_string_append(helper->str, xb_node_get_text(n)); return FALSE; } static gboolean xb_markup_tail_cb(XbNode *n, gpointer user_data) { XbMarkupHelper *helper = (XbMarkupHelper *)user_data; helper->cnt++; /* end */ if (g_strcmp0(xb_node_get_element(n), "em") == 0) { g_string_append(helper->str, "*"); } else if (g_strcmp0(xb_node_get_element(n), "strong") == 0) { g_string_append(helper->str, "**"); } else if (g_strcmp0(xb_node_get_element(n), "code") == 0) { g_string_append(helper->str, "`"); } else if (g_strcmp0(xb_node_get_element(n), "p") == 0) { g_string_append(helper->str, "\n\n"); } /* tail */ if (xb_node_get_tail(n) != NULL) g_string_append(helper->str, xb_node_get_tail(n)); return FALSE; } static void xb_markup_func(void) { gboolean ret; g_autofree gchar *new = NULL; g_autofree gchar *tmp = NULL; g_autoptr(GError) error = NULL; g_autoptr(XbNode) n = NULL; g_autoptr(XbSilo) silo = NULL; XbMarkupHelper helper = { .cnt = 0, .str = g_string_new(NULL), }; const gchar *xml = "<description>" "<p><code>Title</code>:</p>" "<p>There is a <em>slight</em> risk of <strong>death</strong> here<a>!</a></p>" "</description>"; /* import from XML */ silo = xb_silo_new_from_xml(xml, &error); g_assert_no_error(error); g_assert_nonnull(silo); /* ensure we can round-trip */ tmp = xb_silo_to_string(silo, &error); g_assert_no_error(error); g_assert_nonnull(silo); g_debug("\n%s", tmp); n = xb_silo_get_root(silo); g_assert_nonnull(n); new = xb_node_export(n, XB_NODE_EXPORT_FLAG_NONE, &error); g_assert_no_error(error); g_assert_nonnull(new); g_assert_cmpstr(xml, ==, new); /* ensure we can convert this to another format */ ret = xb_node_transmogrify(n, xb_markup_head_cb, xb_markup_tail_cb, &helper); g_assert_true(ret); g_assert_cmpstr(helper.str->str, ==, "`Title`:\n\nThere is a *slight* risk of **death** here!\n\n"); g_assert_cmpint(helper.cnt, ==, 14); g_string_free(helper.str, TRUE); } static void xb_speed_func(void) { XbNode *n; gboolean ret; guint n_components = 5000; g_autofree gchar *tmp_xmlb = g_build_filename(g_get_tmp_dir(), "test.xmlb", NULL); g_autofree gchar *xpath1 = NULL; g_autoptr(GError) error = NULL; g_autoptr(GFile) file = NULL; g_autoptr(GPtrArray) results = NULL; g_autoptr(GString) xml = g_string_new(NULL); g_autoptr(GTimer) timer = g_timer_new(); g_autoptr(XbSilo) silo = NULL; #ifdef __s390x__ /* this is run with qemu and takes too much time */ g_test_skip("s390 too slow, skipping"); return; #endif /* create a huge document */ g_string_append(xml, "<components>"); for (guint i = 0; i < n_components; i++) { g_string_append(xml, "<component type=\"firmware\">"); g_string_append_printf(xml, " <id>%06u.firmware</id>", i); g_string_append(xml, " <name>ColorHug2</name>"); g_string_append(xml, " <summary>Firmware</summary>"); g_string_append(xml, " <description><p>New features!</p></description>"); g_string_append(xml, " <provides>"); g_string_append(xml, " <firmware type=\"flashed\">2082b5e0</firmware>"); g_string_append(xml, " </provides>"); g_string_append(xml, " <requires>"); g_string_append(xml, " <id compare=\"ge\" version=\"0.8.0\">fwupd</id>"); g_string_append(xml, " <firmware compare=\"eq\" version=\"2.0.99\"/>"); g_string_append(xml, " </requires>"); g_string_append(xml, " <url type=\"homepage\">http://com/</url>"); g_string_append(xml, " <metadata_license>CC0-1.0</metadata_license>"); g_string_append(xml, " <project_license>GPL-2.0+</project_license>"); g_string_append(xml, " <updatecontact>richard</updatecontact>"); g_string_append(xml, " <developer_name>Hughski</developer_name>"); g_string_append(xml, " <releases>"); g_string_append( xml, " <release urgency=\"medium\" version=\"2.0.3\" timestamp=\"1429362707\">"); g_string_append( xml, " <description><p>stable:</p><ul><li>Quicker</li></ul></description>"); g_string_append(xml, " </release>"); g_string_append(xml, " </releases>"); g_string_append(xml, "</component>"); } g_string_append(xml, "</components>"); /* import from XML */ silo = xb_silo_new_from_xml(xml->str, &error); g_assert_no_error(error); g_assert_nonnull(silo); file = g_file_new_for_path(tmp_xmlb); ret = xb_silo_save_to_file(silo, file, NULL, &error); g_assert_no_error(error); g_assert_true(ret); g_clear_object(&silo); g_print("import+save: %.3fms\n", g_timer_elapsed(timer, NULL) * 1000); g_timer_reset(timer); /* load from file */ silo = xb_silo_new(); ret = xb_silo_load_from_file(silo, file, XB_SILO_LOAD_FLAG_NONE, NULL, &error); g_assert_no_error(error); g_assert_true(ret); g_print("mmap load: %.3fms\n", g_timer_elapsed(timer, NULL) * 1000); g_timer_reset(timer); /* query best case */ n = xb_silo_query_first(silo, "components/component/id[text()='000000.firmware']", &error); g_assert_no_error(error); g_assert_nonnull(n); g_print("query[first]: %.3fms\n", g_timer_elapsed(timer, NULL) * 1000); g_timer_reset(timer); g_clear_object(&n); /* query worst case */ xpath1 = g_strdup_printf("components/component/id[text()='%06u.firmware']", n_components - 1); n = xb_silo_query_first(silo, xpath1, &error); g_assert_no_error(error); g_assert_nonnull(n); g_print("query[last]: %.3fms\n", g_timer_elapsed(timer, NULL) * 1000); g_timer_reset(timer); g_clear_object(&n); /* query all components */ results = xb_silo_query(silo, "components/component", 0, &error); g_assert_no_error(error); g_assert_nonnull(results); g_assert_cmpint(results->len, ==, n_components); g_print("query[all]: %.3fms\n", g_timer_elapsed(timer, NULL) * 1000); g_timer_reset(timer); /* factorial search */ for (guint i = 0; i < n_components; i += 20) { g_autofree gchar *xpath2 = NULL; xpath2 = g_strdup_printf( "components/component[@type='firmware']/id[text()='%06u.firmware']", i); n = xb_silo_query_first(silo, xpath2, &error); g_assert_no_error(error); g_assert_nonnull(n); g_clear_object(&n); } g_print("query[x%u]: %.3fms\n", n_components, g_timer_elapsed(timer, NULL) * 1000); g_timer_reset(timer); /* factorial search, again */ for (guint i = 0; i < n_components; i += 20) { g_autofree gchar *xpath2 = NULL; xpath2 = g_strdup_printf( "components/component[@type='firmware']/id[text()='%06u.firmware']", i); n = xb_silo_query_first(silo, xpath2, &error); g_assert_no_error(error); g_assert_nonnull(n); g_clear_object(&n); } g_print("query[x%u]: %.3fms\n", n_components, g_timer_elapsed(timer, NULL) * 1000); g_timer_reset(timer); /* create an index */ ret = xb_silo_query_build_index(silo, "components/component/id", NULL, &error); g_assert_no_error(error); g_assert_true(ret); ret = xb_silo_query_build_index(silo, "components/component/id[text()='dave']", NULL, &error); g_assert_no_error(error); g_assert_true(ret); ret = xb_silo_query_build_index(silo, "components/component/DAVE", NULL, &error); g_assert_no_error(error); g_assert_true(ret); ret = xb_silo_query_build_index(silo, "components/component", "type", &error); g_assert_no_error(error); g_assert_true(ret); g_print("create index: %.3fms\n", g_timer_elapsed(timer, NULL) * 1000); g_timer_reset(timer); /* index not found */ n = xb_silo_query_first(silo, "components[text()=$'dave']", &error); g_assert_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_null(n); g_clear_error(&error); /* do the search again, this time with an index */ g_timer_reset(timer); for (guint i = 0; i < n_components; i += 20) { g_autofree gchar *xpath2 = NULL; xpath2 = g_strdup_printf( "components/component[attr($'type')=$'firmware']/id[text()=$'%06u.firmware']", i); n = xb_silo_query_first(silo, xpath2, &error); g_assert_no_error(error); g_assert_nonnull(n); g_clear_object(&n); } g_print("query[x%u]: %.3fms\n", n_components, g_timer_elapsed(timer, NULL) * 1000); } static void xb_speed_precompiled_func(void) { guint n_components = 5000; g_autoptr(GError) error = NULL; g_autoptr(GString) xml = g_string_new(NULL); g_autoptr(GTimer) timer = g_timer_new(); g_autoptr(XbSilo) silo = NULL; g_autoptr(XbQuery) query = NULL; #ifdef __s390x__ g_test_skip("s390 too slow, skipping"); return; #endif /* create a huge document */ g_string_append(xml, "<components>"); for (guint i = 0; i < n_components; i++) { g_string_append(xml, "<component type=\"firmware\">"); g_string_append_printf(xml, " <id>%06u.firmware</id>", i); g_string_append(xml, " <name>ColorHug2</name>"); g_string_append(xml, " <summary>Firmware</summary>"); g_string_append(xml, "</component>"); } g_string_append(xml, "</components>"); silo = xb_silo_new_from_xml(xml->str, &error); g_assert_no_error(error); g_assert_nonnull(silo); query = xb_query_new(silo, "components/component[@type='firmware']", &error); g_assert_no_error(error); g_assert_nonnull(query); g_timer_reset(timer); for (guint i = 0; i < n_components; i += 20) { g_autoptr(GPtrArray) results = NULL; results = xb_silo_query_full(silo, query, &error); g_assert_no_error(error); g_assert_nonnull(results); if (i == 0) g_assert_cmpint(results->len, ==, n_components); } g_print("query_full[x%u]: %.3fms\n", n_components, g_timer_elapsed(timer, NULL) * 1000); } int main(int argc, char **argv) { g_setenv("G_TEST_SRCDIR", SRCDIR, FALSE); g_test_init(&argc, &argv, NULL); /* only critical and error are fatal */ g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); g_setenv("G_MESSAGES_DEBUG", "all", TRUE); setlocale(LC_ALL, ""); /* tests go here */ g_test_add_func("/libxmlb/common", xb_common_func); g_test_add_func("/libxmlb/common{content-type}", xb_common_content_type_func); g_test_add_func("/libxmlb/common{searchv}", xb_common_searchv_func); g_test_add_func("/libxmlb/common{union}", xb_common_union_func); g_test_add_func("/libxmlb/opcodes", xb_predicate_func); g_test_add_func("/libxmlb/opcodes{optimize}", xb_predicate_optimize_func); g_test_add_func("/libxmlb/opcodes{kind}", xb_opcodes_kind_func); g_test_add_func("/libxmlb/stack", xb_stack_func); g_test_add_func("/libxmlb/stack{peek}", xb_stack_peek_func); g_test_add_func("/libxmlb/node{data}", xb_node_data_func); g_test_add_func("/libxmlb/node{export}", xb_node_export_func); g_test_add_func("/libxmlb/node{export-collapse}", xb_node_export_collapse_func); g_test_add_func("/libxmlb/builder", xb_builder_func); g_test_add_func("/libxmlb/builder{comments}", xb_builder_comments_func); g_test_add_func("/libxmlb/builder{cdata}", xb_builder_cdata_func); g_test_add_func("/libxmlb/builder{native-lang}", xb_builder_native_lang_func); g_test_add_func("/libxmlb/builder{native-lang-nested}", xb_builder_native_lang2_func); g_test_add_func("/libxmlb/builder{empty}", xb_builder_empty_func); g_test_add_func("/libxmlb/builder{ensure}", xb_builder_ensure_func); g_test_add_func("/libxmlb/builder{ensure-watch-source}", xb_builder_ensure_watch_source_func); g_test_add_func("/libxmlb/builder{node-vfunc}", xb_builder_node_vfunc_func); g_test_add_func("/libxmlb/builder{node-vfunc-remove}", xb_builder_node_vfunc_remove_func); g_test_add_func("/libxmlb/builder{node-vfunc-depth}", xb_builder_node_vfunc_depth_func); g_test_add_func("/libxmlb/builder{node-vfunc-error}", xb_builder_node_vfunc_error_func); g_test_add_func("/libxmlb/builder{node-vfunc-ignore}", xb_builder_node_vfunc_ignore_func); g_test_add_func("/libxmlb/builder{ignore-invalid}", xb_builder_ignore_invalid_func); g_test_add_func("/libxmlb/builder{custom-mime}", xb_builder_custom_mime_func); g_test_add_func("/libxmlb/builder{chained-adapters}", xb_builder_chained_adapters_func); g_test_add_func("/libxmlb/builder{source-lzma}", xb_builder_source_lzma_func); g_test_add_func("/libxmlb/builder{source-zstd}", xb_builder_source_zstd_func); g_test_add_func("/libxmlb/builder-node", xb_builder_node_func); g_test_add_func("/libxmlb/builder-node/nul", xb_builder_node_nul_func); g_test_add_func("/libxmlb/builder-node{token-max}", xb_builder_node_token_max_func); g_test_add_func("/libxmlb/builder-node{info}", xb_builder_node_info_func); g_test_add_func("/libxmlb/builder-node{literal-text}", xb_builder_node_literal_text_func); g_test_add_func("/libxmlb/builder-node{source-text}", xb_builder_node_source_text_func); g_test_add_func("/libxmlb/markup", xb_markup_func); g_test_add_func("/libxmlb/token-search", xb_manual_token_search_func); g_test_add_func("/libxmlb/xpath", xb_xpath_func); g_test_add_func("/libxmlb/xpath{null-attr}", xb_xpath_null_attr_func); g_test_add_func("/libxmlb/xpath{in}", xb_xpath_in_func); g_test_add_func("/libxmlb/xpath{in-lower}", xb_xpath_in_lower_func); g_test_add_func("/libxmlb/xpath{in-null-needle}", xb_xpath_in_null_needle_func); g_test_add_func("/libxmlb/xpath{stem}", xb_xpath_stem_func); g_test_add_func("/libxmlb/xpath{or-limit}", xb_xpath_or_limit_func); g_test_add_func("/libxmlb/xpath{operator-depth-limit}", xb_xpath_operator_depth_limit_func); g_test_add_func("/libxmlb/xpath{section-limit}", xb_xpath_section_limit_func); g_test_add_func("/libxmlb/builder{strtab-ntags-limit}", xb_builder_strtab_ntags_limit_func); g_test_add_func("/libxmlb/xpath{predicate-limit}", xb_xpath_predicate_limit_func); g_test_add_func("/libxmlb/xpath-query", xb_xpath_query_func); g_test_add_func("/libxmlb/xpath-query{reverse}", xb_xpath_query_reverse_func); g_test_add_func("/libxmlb/xpath-query{force-node-cache}", xb_xpath_query_force_node_cache_func); g_test_add_func("/libxmlb/xpath{helpers}", xb_xpath_helpers_func); g_test_add_func("/libxmlb/xpath{prepared}", xb_xpath_prepared_func); g_test_add_func("/libxmlb/xpath{incomplete}", xb_xpath_incomplete_func); g_test_add_func("/libxmlb/xpath-parent", xb_xpath_parent_func); g_test_add_func("/libxmlb/xpath-glob", xb_xpath_glob_func); g_test_add_func("/libxmlb/xpath-node", xb_xpath_node_func); g_test_add_func("/libxmlb/xpath-parent-subnode", xb_xpath_parent_subnode_func); g_test_add_func("/libxmlb/multiple-roots", xb_builder_multiple_roots_func); g_test_add_func("/libxmlb/single-root", xb_builder_single_root_func); if (g_test_perf()) { g_test_add_func("/libxmlb/threading", xb_threading_func); g_test_add_func("/libxmlb/speed", xb_speed_func); g_test_add_func("/libxmlb/speed-precompiled", xb_speed_precompiled_func); } return g_test_run(); }