blob: 40fe94630d46514a38700d0dc6f942d3f0a6b6aa [file] [log] [blame]
/**
* @file test_xpath.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief Cmocka tests for XPath expression evaluation.
*
* Copyright (c) 2016 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <cmocka.h>
#include "tests/config.h"
#include "libyang.h"
struct state {
struct ly_ctx *ctx;
struct lyd_node *dt;
struct ly_set *set;
};
static const char *data =
"<interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\" xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\""
" xmlns:ip=\"urn:ietf:params:xml:ns:yang:ietf-ip\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\">"
"<interface>"
"<name>iface1</name>"
"<description>iface1 dsc</description>"
"<type>ianaift:ethernetCsmacd</type>"
"<enabled>true</enabled>"
"<link-up-down-trap-enable>disabled</link-up-down-trap-enable>"
"<ip:ipv4>"
"<ip:enabled>true</ip:enabled>"
"<ip:forwarding>true</ip:forwarding>"
"<ip:mtu>68</ip:mtu>"
"<ip:address>"
"<ip:ip>10.0.0.1</ip:ip>"
"<ip:netmask>255.0.0.0</ip:netmask>"
"</ip:address>"
"<ip:address>"
"<ip:ip>172.0.0.1</ip:ip>"
"<ip:prefix-length>16</ip:prefix-length>"
"</ip:address>"
"<ip:neighbor>"
"<ip:ip>10.0.0.2</ip:ip>"
"<ip:link-layer-address>01:34:56:78:9a:bc:de:f0</ip:link-layer-address>"
"</ip:neighbor>"
"</ip:ipv4>"
"<ip:ipv6>"
"<ip:enabled>true</ip:enabled>"
"<ip:forwarding>false</ip:forwarding>"
"<ip:mtu>1280</ip:mtu>"
"<ip:address>"
"<ip:ip>2001:abcd:ef01:2345:6789:0:1:1</ip:ip>"
"<ip:prefix-length>64</ip:prefix-length>"
"</ip:address>"
"<ip:neighbor>"
"<ip:ip>2001:abcd:ef01:2345:6789:0:1:2</ip:ip>"
"<ip:link-layer-address>01:34:56:78:9a:bc:de:f0</ip:link-layer-address>"
"</ip:neighbor>"
"<ip:dup-addr-detect-transmits>52</ip:dup-addr-detect-transmits>"
"<ip:autoconf>"
"<ip:create-global-addresses>true</ip:create-global-addresses>"
"<ip:create-temporary-addresses>false</ip:create-temporary-addresses>"
"<ip:temporary-valid-lifetime>600</ip:temporary-valid-lifetime>"
"<ip:temporary-preferred-lifetime>300</ip:temporary-preferred-lifetime>"
"</ip:autoconf>"
"</ip:ipv6>"
"</interface>"
"<interface>"
"<name>iface2</name>"
"<description>iface2 dsc</description>"
"<type>ianaift:softwareLoopback</type>"
"<enabled>false</enabled>"
"<link-up-down-trap-enable>disabled</link-up-down-trap-enable>"
"<ip:ipv4>"
"<ip:address>"
"<ip:ip>10.0.0.5</ip:ip>"
"<ip:netmask>255.0.0.0</ip:netmask>"
"</ip:address>"
"<ip:address>"
"<ip:ip>172.0.0.5</ip:ip>"
"<ip:prefix-length>16</ip:prefix-length>"
"</ip:address>"
"<ip:neighbor>"
"<ip:ip>10.0.0.1</ip:ip>"
"<ip:link-layer-address>01:34:56:78:9a:bc:de:fa</ip:link-layer-address>"
"</ip:neighbor>"
"</ip:ipv4>"
"<ip:ipv6>"
"<ip:address>"
"<ip:ip>2001:abcd:ef01:2345:6789:0:1:5</ip:ip>"
"<ip:prefix-length>64</ip:prefix-length>"
"</ip:address>"
"<ip:neighbor>"
"<ip:ip>2001:abcd:ef01:2345:6789:0:1:1</ip:ip>"
"<ip:link-layer-address>01:34:56:78:9a:bc:de:fa</ip:link-layer-address>"
"</ip:neighbor>"
"<ip:dup-addr-detect-transmits>100</ip:dup-addr-detect-transmits>"
"<ip:autoconf>"
"<ip:create-global-addresses>true</ip:create-global-addresses>"
"<ip:create-temporary-addresses>false</ip:create-temporary-addresses>"
"<ip:temporary-valid-lifetime>600</ip:temporary-valid-lifetime>"
"<ip:temporary-preferred-lifetime>300</ip:temporary-preferred-lifetime>"
"</ip:autoconf>"
"</ip:ipv6>"
"</interface>"
"</interfaces>"
;
static int
setup_f(void **state)
{
struct state *st;
const char *augschema = "ietf-ip";
const char *typeschema = "iana-if-type";
const char *ietfdir = TESTS_DIR"/schema/yin/ietf/";
const struct lys_module *mod;
(*state) = st = calloc(1, sizeof *st);
if (!st) {
fprintf(stderr, "Memory allocation error.\n");
return -1;
}
/* libyang context */
st->ctx = ly_ctx_new(ietfdir, 0);
if (!st->ctx) {
fprintf(stderr, "Failed to create context.\n");
goto error;
}
/* schema */
mod = ly_ctx_load_module(st->ctx, augschema, NULL);
if (!mod) {
fprintf(stderr, "Failed to load data module \"%s\".\n", augschema);
goto error;
}
lys_features_enable(mod, "*");
mod = ly_ctx_get_module(st->ctx, "ietf-interfaces", NULL, 0);
if (!mod) {
fprintf(stderr, "Failed to get data module \"ietf-interfaces\".\n");
goto error;
}
lys_features_enable(mod, "*");
mod = ly_ctx_load_module(st->ctx, typeschema, NULL);
if (!mod) {
fprintf(stderr, "Failed to load data module \"%s\".\n", typeschema);
goto error;
}
/* data */
st->dt = lyd_parse_mem(st->ctx, data, LYD_XML, LYD_OPT_CONFIG);
if (!st->dt) {
fprintf(stderr, "Failed to build the data tree.\n");
goto error;
}
return 0;
error:
ly_ctx_destroy(st->ctx, NULL);
free(st);
(*state) = NULL;
return -1;
}
static int
teardown_f(void **state)
{
struct state *st = (*state);
lyd_free_withsiblings(st->dt);
ly_set_free(st->set);
ly_ctx_destroy(st->ctx, NULL);
free(st);
(*state) = NULL;
return 0;
}
static void
test_invalid(void **state)
{
struct state *st = (*state);
st->set = lyd_find_path(st->dt, "/:interface/name");
assert_ptr_equal(st->set, NULL);
assert_int_not_equal(ly_errno, 0);
st->set = lyd_find_path(st->dt, "/interface/name[./]");
assert_ptr_equal(st->set, NULL);
assert_int_not_equal(ly_errno, 0);
st->set = lyd_find_path(st->dt, "/interface/name[./.]()");
assert_ptr_equal(st->set, NULL);
assert_int_not_equal(ly_errno, 0);
}
static void
test_simple(void **state)
{
struct state *st = (*state);
st->set = lyd_find_path(st->dt, "/ietf-interfaces:interfaces");
assert_ptr_not_equal(st->set, NULL);
assert_int_equal(st->set->number, 1);
ly_set_free(st->set);
st->set = NULL;
st->set = lyd_find_path(st->dt, "/ietf-interfaces:interfaces/interface");
assert_ptr_not_equal(st->set, NULL);
assert_int_equal(st->set->number, 2);
ly_set_free(st->set);
st->set = NULL;
st->set = lyd_find_path(st->dt, "/ietf-interfaces:interfaces/interface[name='iface1']");
assert_ptr_not_equal(st->set, NULL);
assert_int_equal(st->set->number, 1);
ly_set_free(st->set);
st->set = NULL;
st->set = lyd_find_path(st->dt, "/ietf-interfaces:interfaces/interface[name='iface1']/ietf-ip:ipv4/ietf-ip:address");
assert_ptr_not_equal(st->set, NULL);
assert_int_equal(st->set->number, 2);
ly_set_free(st->set);
st->set = NULL;
st->set = lyd_find_path(st->dt, "/ietf-interfaces:interfaces/interface[name='iface1']/ietf-ip:ipv4/ietf-ip:address[ietf-ip:ip='10.0.0.1']");
assert_ptr_not_equal(st->set, NULL);
assert_int_equal(st->set->number, 1);
ly_set_free(st->set);
st->set = NULL;
}
static void
test_advanced(void **state)
{
struct state *st = (*state);
st->set = lyd_find_path(st->dt, "/ietf-interfaces:interfaces/interface[name='iface1']/ietf-ip:ipv4/*[ietf-ip:ip]");
assert_ptr_not_equal(st->set, NULL);
assert_int_equal(st->set->number, 3);
ly_set_free(st->set);
st->set = NULL;
st->set = lyd_find_path(st->dt, "/ietf-interfaces:interfaces//*[ietf-ip:ip]");
assert_ptr_not_equal(st->set, NULL);
assert_int_equal(st->set->number, 10);
ly_set_free(st->set);
st->set = NULL;
st->set = lyd_find_path(st->dt, "/ietf-interfaces:interfaces//*[ietf-ip:ip[.='10.0.0.1']]");
assert_ptr_not_equal(st->set, NULL);
assert_int_equal(st->set->number, 2);
ly_set_free(st->set);
st->set = NULL;
st->set = lyd_find_path(st->dt, "//ietf-ip:ip[.='10.0.0.1']");
assert_ptr_not_equal(st->set, NULL);
assert_int_equal(st->set->number, 2);
ly_set_free(st->set);
st->set = NULL;
st->set = lyd_find_path(st->dt, "//*[../description]");
assert_ptr_not_equal(st->set, NULL);
assert_int_equal(st->set->number, 14);
ly_set_free(st->set);
st->set = NULL;
st->set = lyd_find_path(st->dt, "//interface[name='iface1']/ietf-ip:ipv4//*");
assert_ptr_not_equal(st->set, NULL);
assert_int_equal(st->set->number, 12);
ly_set_free(st->set);
st->set = NULL;
}
static void
test_functions_operators(void **state)
{
struct state *st = (*state);
st->set = lyd_find_path(st->dt, "/ietf-interfaces:interfaces/interface/name[true() and not(false()) and not(boolean(. != 'iface1'))]");
assert_ptr_not_equal(st->set, NULL);
assert_int_equal(st->set->number, 1);
assert_string_equal(((struct lyd_node_leaf_list *)st->set->set.d[0])->value_str, "iface1");
ly_set_free(st->set);
st->set = NULL;
st->set = lyd_find_path(st->dt, "(/ietf-interfaces:interfaces/interface/name)[round(ceiling(1.8)+0.4)+floor(0.28)]");
assert_ptr_not_equal(st->set, NULL);
assert_int_equal(st->set->number, 1);
assert_string_equal(((struct lyd_node_leaf_list *)st->set->set.d[0])->value_str, "iface2");
ly_set_free(st->set);
st->set = NULL;
st->set = lyd_find_path(st->dt, "/ietf-interfaces:interfaces/interface/type[string-length(substring-after(\"hello12hi\", '12')) != 2 or starts-with(.,'iana') and contains(.,'back') and .=substring-before(concat(string(.),'aab', \"abb\"),'aa')]");
assert_ptr_not_equal(st->set, NULL);
assert_int_equal(st->set->number, 1);
assert_string_equal(((struct lyd_node_leaf_list *)st->set->set.d[0])->value_str, "iana-if-type:softwareLoopback");
ly_set_free(st->set);
st->set = NULL;
st->set = lyd_find_path(st->dt, "//*[ietf-ip:neighbor/ietf-ip:link-layer-address = translate(normalize-space('\t\n01 .34 .56\t.78.9a\n\r.bc.de.f0 \t'), '. ', ':')]");
assert_ptr_not_equal(st->set, NULL);
assert_int_equal(st->set->number, 2);
ly_set_free(st->set);
st->set = NULL;
st->set = lyd_find_path(st->dt, "(//ietf-ip:ip)[position() = last()]");
assert_ptr_not_equal(st->set, NULL);
assert_int_equal(st->set->number, 1);
assert_string_equal(((struct lyd_node_leaf_list *)st->set->set.d[0])->value_str, "2001:abcd:ef01:2345:6789:0:1:1");
ly_set_free(st->set);
st->set = NULL;
st->set = lyd_find_path(st->dt, "(//ietf-ip:ip)[count(//*[.='52'])]");
assert_ptr_not_equal(st->set, NULL);
assert_int_equal(st->set->number, 1);
assert_string_equal(((struct lyd_node_leaf_list *)st->set->set.d[0])->value_str, "10.0.0.1");
ly_set_free(st->set);
st->set = NULL;
st->set = lyd_find_path(st->dt, "//*[local-name()='autoconf' and namespace-uri()='urn:ietf:params:xml:ns:yang:ietf-ip']");
assert_ptr_not_equal(st->set, NULL);
assert_int_equal(st->set->number, 2);
ly_set_free(st->set);
st->set = NULL;
st->set = lyd_find_path(st->dt, "//interface[name='iface2']//. | //ip | //interface[number((1 mod (20 - 15)) div 1)]//.");
assert_ptr_not_equal(st->set, NULL);
assert_int_equal(st->set->number, 68);
ly_set_free(st->set);
st->set = NULL;
st->set = lyd_find_path(st->dt, "//ietf-ip:ip[position() mod 2 = 1] | //ietf-ip:ip[position() mod 2 = 0]");
assert_ptr_not_equal(st->set, NULL);
assert_int_equal(st->set->number, 10);
assert_string_equal(((struct lyd_node_leaf_list *)st->set->set.d[0])->value_str, "10.0.0.1");
assert_string_equal(((struct lyd_node_leaf_list *)st->set->set.d[1])->value_str, "172.0.0.1");
assert_string_equal(((struct lyd_node_leaf_list *)st->set->set.d[2])->value_str, "10.0.0.2");
assert_string_equal(((struct lyd_node_leaf_list *)st->set->set.d[7])->value_str, "10.0.0.1");
assert_string_equal(((struct lyd_node_leaf_list *)st->set->set.d[9])->value_str, "2001:abcd:ef01:2345:6789:0:1:1");
ly_set_free(st->set);
st->set = NULL;
st->set = lyd_find_path(st->dt, "(//*)[1] | (//*)[last()] | (//*)[10] | (//*)[8]//.");
assert_ptr_not_equal(st->set, NULL);
assert_int_equal(st->set->number, 15);
ly_set_free(st->set);
st->set = NULL;
}
int main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_invalid, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_simple, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_advanced, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_functions_operators, setup_f, teardown_f),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}