text: /**
   * @file measure_performance.c
   * @author Michal Vasko <mvasko@cesnet.cz>
   * @brief File measure performance of the sysrepo operations
   *
   * @copyright
   * Copyright (c) 2018 - 2021 Deutsche Telekom AG.
   * Copyright (c) 2018 - 2021 CESNET, z.s.p.o.
   *
   * This source code is licensed under BSD 3-Clause License (the "License").
   * You may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     https://opensource.org/licenses/BSD-3-Clause
   */
  
  #define _GNU_SOURCE
  
  #include <stdlib.h>
  #include <stdio.h>
  #include <sys/time.h>
  #include <unistd.h>
  #include <string.h>
  #include <setjmp.h>
  #include <cmocka.h>
  #include <stdbool.h>
  #include <libyang/libyang.h>
  
  #include "tests/config.h"
  #include "sysrepo.h"
  
  /* Constants defining how many times the operation is performed to compute an average ops/sec */
  
  /**@brief all operations except commit */
  #define OP_COUNT 50000
  
  /**@brief used with larger data files */
  #define OP_COUNT_LOW 30000
  
  /**@brief constant for commit operation */
  #define OP_COUNT_COMMIT 1000
  
  #define TEST_SCHEMA_SEARCH_DIR "/home/vasko/Documents/sysrepo/build/repository/yang/"
  #define TEST_DATA_PREFIX "/dev/shm/sr_"
  #define SR_RUNNING_FILE_EXT ".running"
  #define EXAMPLE_MODULE_DATA_FILE_NAME TEST_DATA_PREFIX "example-module" SR_RUNNING_FILE_EXT
  
  int instance_cnt = 1;
  
  /* Computes diff of two timeval structures
   * @see http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html
   */
  int
  timeval_subtract (struct timeval *result, struct timeval *x, struct timeval *y)
  {
      /* Perform the carry for the later subtraction by updating y. */
      if (x->tv_usec < y->tv_usec) {
          int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
          y->tv_usec -= 1000000 * nsec;
          y->tv_sec += nsec;
      }
      if (x->tv_usec - y->tv_usec > 1000000) {
          int nsec = (x->tv_usec - y->tv_usec) / 1000000;
          y->tv_usec += 1000000 * nsec;
          y->tv_sec -= nsec;
      }
  
      /* Compute the time remaining to wait.
       tv_usec is certainly positive. */
      result->tv_sec = x->tv_sec - y->tv_sec;
      result->tv_usec = x->tv_usec - y->tv_usec;
  
      /* Return 1 if result is negative. */
      return x->tv_sec < y->tv_sec;
  }
  
  typedef struct test_s{
      void (*function)(void **, int, int *);
      char *op_name;
      int op_count;
      void (*setup)(void **);
      void (*teardown)(void **);
  } test_t;
  
  void
  print_measure_header(const char *title)
  {
      printf("\n\n\t\t%s", title);
      printf("\n%-32s| %10s | %10s | %13s | %10s | %10s\n",
              "Operation", "ops/sec", "items/op", "ops performed", "items/sec", "test time");
      printf("---------------------------------------------------------------------------------------------------\n");
  }
  
  /**
   * @brief Handles the process of time measurement.
   * 1. runs setup
   * 2. starts timer
   * 3. execute function being measured
   * 4. stops timer
   * 5. runs cleanup
   * 6. computes and prints the output
   *
   * Function being measured accepts:
   * - the state argument created by setup,
   * - the repeat count - how man times the operation should be performed
   * - pointer where the number of influenced items is returned
   */
  void
  measure(void (*func)(void **, int, int *), const char *name, int op_count, void (*setup)(void **), void (*teardown)(void **))
  {
      struct timeval tv1 = {0, };
      struct timeval tv2 = {0, };
      struct timeval diff = {0, };
      void *state = NULL;
      double seconds = 0.0;
      int items = 0;
  
      setup(&state);
  
      gettimeofday(&tv1, NULL);
  
      func(&state, op_count, &items);
  
      gettimeofday(&tv2, NULL);
      teardown(&state);
  
      timeval_subtract(&diff, &tv2, &tv1);
  
      seconds = diff.tv_sec + 0.000001 * diff.tv_usec;
      printf("%-32s| %10.0f | %10d | %13d | %10.0f | %10.2f\n",
              name, items ? ((double) op_count) / seconds : 0, items, op_count, items ? ((double) op_count * items) / seconds : 0, seconds);
  }
  
  void
  sysrepo_setup(void **state)
  {
  
      sr_conn_ctx_t *conn = NULL;
      int rc = SR_ERR_OK;
  
      /* turn off all logging */
      sr_log_stderr(SR_LL_WRN);
      sr_log_syslog("perf_test", SR_LL_NONE);
  
      /* connect to sysrepo */
      rc = sr_connect(SR_CONN_CACHE_RUNNING, &conn);
      assert_int_equal(rc, SR_ERR_OK);
  
      *state = (void *)conn;
  
  }
  
  void
  sysrepo_teardown(void **state)
  {
      sr_conn_ctx_t *conn = *state;
  
      assert_non_null(conn);
  
      /* disconnect from sysrepo */
      sr_disconnect(conn);
  }
  
  void
  libyang_setup(void **state)
  {
      struct ly_ctx *ctx;
  
      ly_ctx_new(TEST_SCHEMA_SEARCH_DIR, 0, &ctx);
      const struct lys_module *module = ly_ctx_load_module(ctx, "example-module", NULL, NULL);
  
      assert_non_null(module);
      *state = (void *) ctx;
  }
  
  void
  libyang_teardown(void **state)
  {
      struct ly_ctx *ctx = *state;
  
      ly_ctx_destroy(ctx);
  }
  
  typedef struct dp_setup_s {
      sr_subscription_ctx_t *subs;
      sr_conn_ctx_t *conn;
      sr_session_ctx_t *session;
      size_t if_count;
  } dp_setup_t;
  
  int
  data_provide_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *module_name, const char *path,
          const char *request_xpath, uint32_t request_id, struct lyd_node **parent, void *private_data)
  {
      size_t if_count = *((size_t *) private_data);
      int rc = SR_ERR_OK;
      struct lyd_node *list, *stats;
      char *xpath;
  
      (void)session;
      (void)sub_id;
      (void)module_name;
      (void)path;
      (void)request_xpath;
      (void)request_id;
  
      for (size_t i = 0; i < if_count; i++) {
          asprintf(&xpath, "interface[name='eth%zu']", i);
          assert_int_equal(LY_SUCCESS, lyd_new_path(*parent, NULL, xpath, NULL, 0, &list));
          free(xpath);
          assert_int_equal(LY_SUCCESS, lyd_new_term(list, NULL, "oper-status", "up", 0, NULL));
  
          assert_int_equal(LY_SUCCESS, lyd_new_inner(list, NULL, "statistics", 0, &stats));
          assert_int_equal(LY_SUCCESS, lyd_new_term(stats, NULL, "in-octets", "456213", 0, NULL));
          assert_int_equal(LY_SUCCESS, lyd_new_term(stats, NULL, "in-unicast-pkts", "45213", 0, NULL));
          assert_int_equal(LY_SUCCESS, lyd_new_term(stats, NULL, "in-broadcast-pkts", "4213", 0, NULL));
      }
  
      return rc;
  }
  
  void
  data_provide_setup(void **state)
  {
  
      dp_setup_t *dp_setup = calloc(1, sizeof(*dp_setup));
  
      assert_non_null(dp_setup);
      int rc = SR_ERR_OK;
  
      /* turn off all logging */
      sr_log_stderr(SR_LL_NONE);
      sr_log_syslog(NULL, SR_LL_NONE);
  
      /* connect to sysrepo */
      rc = sr_connect(SR_CONN_DEFAULT, &dp_setup->conn);
      assert_int_equal(rc, SR_ERR_OK);
  
      rc = sr_session_start(dp_setup->conn, SR_DS_RUNNING, &dp_setup->session);
      assert_int_equal(rc, SR_ERR_OK);
  
      rc = sr_oper_get_items_subscribe(dp_setup->session, "ietf-interfaces", "/ietf-interfaces:interfaces-state/interface",
              data_provide_cb, &dp_setup->if_count, SR_SUBSCR_DEFAULT, &dp_setup->subs);
      assert_int_equal(rc, SR_ERR_OK);
  
      sr_session_switch_ds(dp_setup->session, SR_DS_OPERATIONAL);
  
      *state = (void *) dp_setup;
  }
  
  void
  data_provide_teardown(void **state)
  {
      dp_setup_t *dp_setup = (dp_setup_t *) *state;
  
      sr_unsubscribe(dp_setup->subs);
      sr_session_stop(dp_setup->session);
      sr_disconnect(dp_setup->conn);
  
  }
  
  static void
  perf_data_provide_test(void **state, int op_num, int *items)
  {
      dp_setup_t *dp_setup = *state;
  
      assert_non_null(dp_setup);
  
      sr_val_t *value = NULL;
      size_t val_cnt = 0;
      int rc = 0;
  
      dp_setup->if_count = instance_cnt;
  
      /* perform get call*/
      for (int i = 0; i < op_num; i++) {
          val_cnt = 0;
          value = NULL;
  
          rc = sr_get_items(dp_setup->session, "/ietf-interfaces:interfaces-state/interface/statistics//*", 0, 0, &value, &val_cnt);
          assert_int_equal(SR_ERR_OK, rc);
  
          sr_free_values(value, val_cnt);
      }
  
      /* stop the session */
      *items = val_cnt;
  }
  
  static void
  perf_get_item_test(void **state, int op_num, int *items)
  {
      sr_conn_ctx_t *conn = *state;
  
      assert_non_null(conn);
  
      sr_session_ctx_t *session = NULL;
      sr_val_t *value = NULL;
      int rc = 0;
  
      /* start a session */
      rc = sr_session_start(conn, SR_DS_RUNNING, &session);
      assert_int_equal(rc, SR_ERR_OK);
  
      /* perform a get-item request */
      for (int i = 0; i < op_num; i++) {
  
          /* existing leaf */
          rc = sr_get_item(session, "/example-module:container/list[key1='key1'][key2='key2']/leaf", 0, &value);
          assert_int_equal(rc, SR_ERR_OK);
          assert_non_null(value);
          assert_int_equal(SR_STRING_T, value->type);
          sr_free_val(value);
      }
  
      /* stop the session */
      rc = sr_session_stop(session);
      assert_int_equal(rc, SR_ERR_OK);
      *items = 1;
  }
  
  static void
  perf_get_item_first_test(void **state, int op_num, int *items)
  {
      sr_conn_ctx_t *conn = *state;
  
      assert_non_null(conn);
  
      sr_session_ctx_t *session = NULL;
      sr_val_t *value = NULL;
      int rc = 0;
  
      /* start a session */
      rc = sr_session_start(conn, SR_DS_RUNNING, &session);
      assert_int_equal(rc, SR_ERR_OK);
  
      /* perform a get-item request */
      for (int i = 0; i < op_num; i++) {
  
          /* existing first node in data tree */
          rc = sr_get_item(session, "/example-module:container", 0, &value);
          if (SR_ERR_OK == rc) {
              *items = 1;
              assert_non_null(value);
              assert_int_equal(SR_CONTAINER_T, value->type);
              sr_free_val(value);
          } else {
              *items = 0;
          }
      }
  
      /* stop the session */
      rc = sr_session_stop(session);
      assert_int_equal(rc, SR_ERR_OK);
  }
  
  static void
  perf_get_item_with_data_load_test(void **state, int op_num, int *items)
  {
      sr_conn_ctx_t *conn = *state;
  
      assert_non_null(conn);
  
      sr_session_ctx_t *session = NULL;
      sr_val_t *value = NULL;
      int rc = 0;
  
      /* perform session_start, get-item, session-stop requests */
      for (int i = 0; i < op_num; i++) {
          /* start a session */
          rc = sr_session_start(conn, SR_DS_RUNNING, &session);
          assert_int_equal(rc, SR_ERR_OK);
  
          /* existing leaf */
          rc = sr_get_item(session, "/example-module:container/list[key1='key1'][key2='key2']/leaf", 0, &value);
          assert_int_equal(rc, SR_ERR_OK);
          assert_non_null(value);
          assert_int_equal(SR_STRING_T, value->type);
          sr_free_val(value);
  
          /* stop the session */
          rc = sr_session_stop(session);
          assert_int_equal(rc, SR_ERR_OK);
      }
  
      *items = 1;
  }
  
  static void
  perf_get_items_test(void **state, int op_num, int *items)
  {
      sr_conn_ctx_t *conn = *state;
  
      assert_non_null(conn);
  
      sr_session_ctx_t *session = NULL;
      sr_val_t *values = NULL;
      size_t count = 0;
      int rc = 0;
  
      /* start a session */
      rc = sr_session_start(conn, SR_DS_RUNNING, &session);
      assert_int_equal(rc, SR_ERR_OK);
  
      /* perform a get-items request */
      for (int i = 0; i < op_num; i++) {
          /* existing leaf */
          rc = sr_get_items(session, "/example-module:container/list/leaf", 0, 0, &values, &count);
          assert_int_equal(SR_ERR_OK, rc);
          sr_free_values(values, count);
      }
  
      /* stop the session */
      rc = sr_session_stop(session);
      assert_int_equal(rc, SR_ERR_OK);
      *items = count;
  }
  
  static void
  perf_get_items_iter_test(void **state, int op_num, int *items)
  {
      sr_conn_ctx_t *conn = *state;
  
      assert_non_null(conn);
      (void)op_num;
  
      *items = 0;
  }
  
  static size_t
  get_nodes_cnt(struct lyd_node *trees)
  {
      struct lyd_node *root, *elem;
      size_t count = 0;
  
      LY_LIST_FOR(trees, root) {
          LYD_TREE_DFS_BEGIN(root, elem) {
              ++count;
              LYD_TREE_DFS_END(root, elem);
          }
      }
  
      return count;
  }
  
  static void
  perf_get_ietf_intefaces_test(void **state, int op_num, int *items)
  {
      sr_conn_ctx_t *conn = *state;
  
      assert_non_null(conn);
  
      sr_session_ctx_t *session = NULL;
      struct lyd_node *data;
      int rc = 0;
  
      /* start a session */
      rc = sr_session_start(conn, SR_DS_RUNNING, &session);
      assert_int_equal(rc, SR_ERR_OK);
  
      /* perform a get-items_iter request */
      size_t count = 0;
  
      for (int i = 0; i < op_num; i++) {
          count = 0;
          /* existing leaf */
          rc = sr_get_data(session, "/ietf-interfaces:interfaces/*", 0, 0, 0, &data);
          assert_int_equal(SR_ERR_OK, rc);
          count += get_nodes_cnt(data);
          lyd_free_all(data);
      }
  
      /* stop the session */
      rc = sr_session_stop(session);
      assert_int_equal(rc, SR_ERR_OK);
      *items = count;
  }
  
  static void
  perf_get_subtree_test(void **state, int op_num, int *items)
  {
      sr_conn_ctx_t *conn = *state;
  
      assert_non_null(conn);
  
      sr_session_ctx_t *session = NULL;
      struct lyd_node *tree = NULL;
      int rc = 0;
  
      /* start a session */
      rc = sr_session_start(conn, SR_DS_RUNNING, &session);
      assert_int_equal(rc, SR_ERR_OK);
  
      /* perform a get-item request */
      for (int i = 0; i < op_num; i++) {
          /* existing leaf */
          rc = sr_get_subtree(session, "/example-module:container/list[key1='key1'][key2='key2']/leaf", 0, &tree);
          assert_int_equal(rc, SR_ERR_OK);
          assert_non_null(tree);
          assert_int_equal(LY_TYPE_STRING, ((struct lysc_node_leaf *)tree->schema)->type->basetype);
          lyd_free_tree(tree);
      }
  
      /* stop the session */
      rc = sr_session_stop(session);
      assert_int_equal(rc, SR_ERR_OK);
      *items = 1;
  }
  
  static void
  perf_get_subtree_with_data_load_test(void **state, int op_num, int *items)
  {
      sr_conn_ctx_t *conn = *state;
  
      assert_non_null(conn);
  
      sr_session_ctx_t *session = NULL;
      struct lyd_node *tree = NULL;
      int rc = 0;
  
      /* perform session_start, get-item, session-stop requests */
      for (int i = 0; i < op_num; i++) {
          /* start a session */
          rc = sr_session_start(conn, SR_DS_RUNNING, &session);
          assert_int_equal(rc, SR_ERR_OK);
  
          /* existing leaf */
          rc = sr_get_subtree(session, "/example-module:container/list[key1='key1'][key2='key2']/leaf", 0, &tree);
          assert_int_equal(rc, SR_ERR_OK);
          assert_non_null(tree);
          assert_int_equal(LY_TYPE_STRING, ((struct lysc_node_leaf *)tree->schema)->type->basetype);
          lyd_free_tree(tree);
  
          /* stop the session */
          rc = sr_session_stop(session);
          assert_int_equal(rc, SR_ERR_OK);
      }
  
      *items = 1;
  }
  
  static void
  perf_get_subtrees_test(void **state, int op_num, int *items)
  {
      sr_conn_ctx_t *conn = *state;
  
      assert_non_null(conn);
  
      sr_session_ctx_t *session = NULL;
      struct lyd_node *trees = NULL;
      size_t count = 0;
      int rc = 0;
  
      /* start a session */
      rc = sr_session_start(conn, SR_DS_RUNNING, &session);
      assert_int_equal(rc, SR_ERR_OK);
  
      /* perform a get-subtrees request */
      for (int i = 0; i < op_num; i++) {
          count = 0;
          /* existing leaf */
          rc = sr_get_data(session, "/example-module:container/list/leaf", 0, 0, 0, &trees);
          assert_int_equal(SR_ERR_OK, rc);
          /* skip 2 keys */
          assert_string_equal(lyd_child(lyd_child(trees))->next->next->schema->name, "leaf");
          count += get_nodes_cnt(trees);
          lyd_free_all(trees);
      }
  
      /* stop the session */
      rc = sr_session_stop(session);
      assert_int_equal(rc, SR_ERR_OK);
      *items = count;
  }
  
  static void
  perf_get_ietf_intefaces_tree_test(void **state, int op_num, int *items)
  {
      sr_conn_ctx_t *conn = *state;
  
      assert_non_null(conn);
  
      sr_session_ctx_t *session = NULL;
      struct lyd_node *trees = NULL;
      size_t total_cnt = 0;
      int rc = 0;
  
      /* start a session */
      rc = sr_session_start(conn, SR_DS_RUNNING, &session);
      assert_int_equal(rc, SR_ERR_OK);
  
      /* perform a get-subtrees request */
      for (int i = 0; i < op_num; i++) {
          rc = sr_get_data(session, "/ietf-interfaces:interfaces/.", 0, 0, 0, &trees);
          assert_int_equal(rc, SR_ERR_OK);
          if (0 == i) {
              total_cnt = get_nodes_cnt(trees);
          }
          lyd_free_all(trees);
      }
  
      /* stop the session */
      rc = sr_session_stop(session);
      assert_int_equal(rc, SR_ERR_OK);
      *items = total_cnt;
  }
  
  static void
  perf_set_delete_test(void **state, int op_num, int *items)
  {
      sr_conn_ctx_t *conn = *state;
  
      assert_non_null(conn);
      (void)op_num;
  // sr_session_ctx_t *session = NULL;
  // char xpath[PATH_MAX] = { 0, };
  // int rc = 0;
  //
  ///* start a session */
  // rc = sr_session_start(conn, SR_DS_RUNNING, &session);
  // assert_int_equal(rc, SR_ERR_OK);
  //
  ///* perform edit, commit request */
  // sr_val_t value = {0,};
  // for (size_t i = 0; i < op_num; i++) {
  //
  ///* set a list instance */
  // sprintf(xpath, "/example-module:container/list[key1='set_del'][key2='set_1']/leaf");
  // value.type = SR_STRING_T;
  // value.data.string_val = strdup("Leaf");
  // assert_non_null(value.data.string_val);
  // rc = sr_set_item(session, xpath, &value, SR_EDIT_DEFAULT);
  // assert_int_equal(rc, SR_ERR_OK);
  //
  ///* delete a list instance */
  // sprintf(xpath, "/example-module:container/list[key1='set_del'][key2='set_1']");
  // rc = sr_delete_item(session, xpath, SR_EDIT_DEFAULT);
  // assert_int_equal(rc, SR_ERR_OK);
  // }
  //
  ///* stop the session */
  // rc = sr_session_stop(session);
  // assert_int_equal(rc, SR_ERR_OK);
  //
  // *items = 1 /* list instances */ * 3 /* leaves */ * 2 /* set + delete */ ;
      *items = 0;
  }
  
  static void
  perf_set_delete_100_test(void **state, int op_num, int *items)
  {
      sr_conn_ctx_t *conn = *state;
  
      assert_non_null(conn);
      (void)op_num;
  // sr_session_ctx_t *session = NULL;
  // char xpath[PATH_MAX] = { 0, };
  // int rc = 0;
  //
  ///* start a session */
  // rc = sr_session_start(conn, SR_DS_RUNNING, &session);
  // assert_int_equal(rc, SR_ERR_OK);
  //
  ///* perform edit, commit request */
  // sr_val_t value = {0,};
  // for (size_t i = 0; i < op_num; i++) {
  //
  ///* set 100 list instances */
  // for (size_t j = 0; j <= 100; j++) {
  // sprintf(xpath, "/example-module:container/list[key1='set_del'][key2='set_%zu']/leaf", j);
  // value.type = SR_STRING_T;
  // value.data.string_val = strdup("Leaf");
  // assert_non_null(value.data.string_val);
  // rc = sr_set_item(session, xpath, &value, SR_EDIT_DEFAULT);
  // assert_int_equal(rc, SR_ERR_OK);
  // }
  //
  ///* delete 100 list instances */
  // for (size_t j = 0; j <= 100; j++) {
  // sprintf(xpath, "/example-module:container/list[key1='set_del'][key2='set_%zu']", j);
  // rc = sr_delete_item(session, xpath, SR_EDIT_DEFAULT);
  // assert_int_equal(rc, SR_ERR_OK);
  // }
  // }
  //
  ///* stop the session */
  // rc = sr_session_stop(session);
  // assert_int_equal(rc, SR_ERR_OK);
  //
  // *items = 100 /* list instances */ * 3 /* leaves */ * 2 /* set + delete */ ;
      *items = 0;
  }
  
  static void
  perf_commit_test(void **state, int op_num, int *items)
  {
      sr_conn_ctx_t *conn = *state;
  
      assert_non_null(conn);
      sr_session_ctx_t *session = NULL;
      int rc = 0;
  
      /* start a session */
      rc = sr_session_start(conn, SR_DS_RUNNING, &session);
      assert_int_equal(rc, SR_ERR_OK);
  
      /* perform edit, commit request */
      bool even = true;
  
      for (int i = 0; i < op_num; i++) {
          if (even) {
              rc = sr_delete_item(session, "/example-module:container/list[key1='key1'][key2='key2']/leaf", SR_EDIT_DEFAULT);
          } else {
              sr_val_t value = {0, };
              value.type = SR_STRING_T;
              value.data.string_val = strdup("Leaf");
              assert_non_null(value.data.string_val);
              rc = sr_set_item(session, "/example-module:container/list[key1='key1'][key2='key2']/leaf", &value, SR_EDIT_DEFAULT);
          }
          assert_int_equal(rc, SR_ERR_OK);
          rc = sr_apply_changes(session, 0);
          assert_int_equal(rc, SR_ERR_OK);
          even = !even;
      }
  
      /* stop the session */
      rc = sr_session_stop(session);
      assert_int_equal(rc, SR_ERR_OK);
      *items = 1;
  }
  
  static int
  test_rpc_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *op_path, const sr_val_t *input, const size_t input_cnt,
          sr_event_t event, uint32_t request_id, sr_val_t **output, size_t *output_cnt, void *private_data)
  {
      sr_val_t *v = NULL;
  
      (void)session;
      (void)sub_id;
      (void)op_path;
      (void)input;
      (void)event;
      (void)request_id;
      (void)private_data;
  
      /* check input */
      assert_true(input_cnt > 0);
  
      v = calloc(1, sizeof *v);
      assert_non_null(v);
  
      v->xpath = strdup("/test-module:activate-software-image/status");
      v->type = SR_STRING_T;
      v->data.string_val = strdup("The image acmefw-2.3 is being installed.");
  
      *output = v;
      *output_cnt = 1;
  
      return SR_ERR_OK;
  }
  
  static int
  test_dummy_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *module_name, const char *xpath, sr_event_t event,
          uint32_t request_id, void *private_data)
  {
      (void)session;
      (void)sub_id;
      (void)module_name;
      (void)xpath;
      (void)event;
      (void)request_id;
      (void)private_data;
  
      return SR_ERR_OK;
  }
  
  static void
  perf_rpc_test(void **state, int op_num, int *items)
  {
      sr_conn_ctx_t *conn = *state;
  
      assert_non_null(conn);
      sr_session_ctx_t *session = NULL;
      sr_subscription_ctx_t *subscription = NULL;
      sr_val_t input = {0, };
      sr_val_t *output = NULL;
      size_t output_cnt = 0;
      int rc = 0;
  
      /* start a session */
      rc = sr_session_start(conn, SR_DS_RUNNING, &session);
      assert_int_equal(rc, SR_ERR_OK);
  
      /* subscribe for RPC */
      rc = sr_rpc_subscribe(session, "/test-module:activate-software-image", test_rpc_cb, NULL, 0, SR_SUBSCR_DEFAULT,
              &subscription);
      assert_int_equal(rc, SR_ERR_OK);
  
      /* subscribe to a default leaf so that it is present in operational */
      rc = sr_module_change_subscribe(session, "test-module", "/test-module:top-level-default", test_dummy_cb, NULL, 0,
              SR_SUBSCR_CTX_REUSE, &subscription);
      assert_int_equal(rc, SR_ERR_OK);
  
      input.xpath = "/test-module:activate-software-image/image-name";
      input.type = SR_STRING_T;
      input.data.string_val = "acmefw-2.3";
  
      /* send the RPC */
      for (int i = 0; i < op_num; i++) {
          rc = sr_rpc_send(session, "/test-module:activate-software-image", &input, 1, 0, &output, &output_cnt);
          assert_int_equal(rc, SR_ERR_OK);
          assert_true(output_cnt > 0);
          sr_free_values(output, output_cnt);
      }
  
      /* unsubscribe from RPCs */
      rc = sr_unsubscribe(subscription);
      assert_int_equal(rc, SR_ERR_OK);
  
      /* stop the session */
      rc = sr_session_stop(session);
      assert_int_equal(rc, SR_ERR_OK);
      *items = 1;
  }
  
  static void
  test_event_notif_link_discovery_cb(sr_session_ctx_t *session, const sr_ev_notif_type_t notif_type, uint32_t sub_id,
          const char *path, const sr_val_t *values, const size_t values_cnt, struct timespec *timestamp, void *private_data)
  {
      (void)session;
      (void)notif_type;
      (void)sub_id;
      (void)path;
      (void)values;
      (void)timestamp;
      (void)private_data;
      assert_true(values_cnt > 0);
  }
  
  static void
  perf_ev_notification_test(void **state, int op_num, int *items, bool ephemeral)
  {
      sr_conn_ctx_t *conn = *state;
  
      assert_non_null(conn);
      sr_session_ctx_t *session = NULL;
      sr_subscription_ctx_t *subscription = NULL;
      sr_val_t values[4] = {{0, }, };
      int rc = 0;
  
      if (ephemeral) {
          rc = sr_set_module_replay_support(conn, "test-module", 0);
          assert_int_equal(rc, SR_ERR_OK);
      } else {
          rc = sr_set_module_replay_support(conn, "test-module", 1);
          assert_int_equal(rc, SR_ERR_OK);
      }
  
      /* start a session */
      rc = sr_session_start(conn, SR_DS_RUNNING, &session);
      assert_int_equal(rc, SR_ERR_OK);
  
      /* enable notification buffering */
      rc = sr_session_notif_buffer(session);
      assert_int_equal(rc, SR_ERR_OK);
  
      /* subscribe for event notification */
      rc = sr_event_notif_subscribe(session, "test-module", "/test-module:link-discovered", 0, 0,
              test_event_notif_link_discovery_cb, NULL, SR_SUBSCR_DEFAULT, &subscription);
      assert_int_equal(rc, SR_ERR_OK);
  
      /* send event notification - link discovery */
      values[0].xpath = "/test-module:link-discovered/source/address";
      values[0].type = SR_STRING_T;
      values[0].data.string_val = "10.10.1.5";
      values[1].xpath = "/test-module:link-discovered/source/interface";
      values[1].type = SR_STRING_T;
      values[1].data.string_val = "eth1";
      values[2].xpath = "/test-module:link-discovered/destination/address";
      values[2].type = SR_STRING_T;
      values[2].data.string_val = "10.10.1.8";
      values[3].xpath = "/test-module:link-discovered/destination/interface";
      values[3].type = SR_STRING_T;
      values[3].data.string_val = "eth0";
  
      /* send the notification */
      for (int i = 0; i < op_num; i++) {
          rc = sr_event_notif_send(session, "/test-module:link-discovered", values, 4, 0, 0);
          assert_int_equal(rc, SR_ERR_OK);
      }
  
      /* unsubscribe from notifications */
      rc = sr_unsubscribe(subscription);
      assert_int_equal(rc, SR_ERR_OK);
  
      /* stop the session */
      rc = sr_session_stop(session);
      assert_int_equal(rc, SR_ERR_OK);
      *items = 1;
  }
  
  static void
  perf_ev_notification_ephemeral_test(void **state, int op_num, int *items)
  {
      perf_ev_notification_test(state, op_num, items, true);
  }
  
  static void
  perf_ev_notification_store_test(void **state, int op_num, int *items)
  {
      perf_ev_notification_test(state, op_num, items, false);
  }
  
  static void
  perf_libyang_get_node(void **state, int op_num, int *items)
  {
      struct ly_ctx *ctx = *state;
      struct lyd_node *root;
      struct ly_set *set;
  
      assert_non_null(ctx);
      assert_int_equal(LY_SUCCESS, lyd_parse_data_path(ctx, EXAMPLE_MODULE_DATA_FILE_NAME, LYD_LYB,
              LYD_PARSE_ONLY | LYD_PARSE_NO_STATE | LYD_PARSE_STRICT, 0, &root));
  
      /* perform a lyd_get_node op */
      for (int i = 0; i < op_num; i++) {
  
          /* existing leaf */
          assert_int_equal(LY_SUCCESS, lyd_find_xpath(root, "/example-module:container/list[key1='key1'][key2='key2']/leaf",
                  &set));
          ly_set_free(set, NULL);
      }
  
      lyd_free_all(root);
      *items = 1;
  }
  
  static void
  perf_libyang_get_all_list(void **state, int op_num, int *items)
  {
      struct ly_ctx *ctx = *state;
      struct lyd_node *root;
      struct ly_set *set;
  
      assert_non_null(ctx);
  
      assert_int_equal(LY_SUCCESS, lyd_parse_data_path(ctx, EXAMPLE_MODULE_DATA_FILE_NAME, LYD_LYB,
              LYD_PARSE_ONLY | LYD_PARSE_NO_STATE | LYD_PARSE_STRICT, 0, &root));
  
      /* perform a lyd_get_node op */
      for (int i = 0; i < op_num; i++) {
  
          /* existing leaf */
          assert_int_equal(LY_SUCCESS, lyd_find_xpath(root, "/example-module:container/list/leaf", &set));
          *items = set->count;
          ly_set_free(set, NULL);
      }
  
      lyd_free_all(root);
  
  }
  
  void
  test_perf(test_t *ts, int test_count, const char *title, int selection)
  {
      print_measure_header(title);
      for (int i = 0; i < test_count; i++) {
          test_t *t = &ts[i];
          if ((-1 == selection) || (i == selection)) {
              measure(t->function, t->op_name, t->op_count, t->setup, t->teardown);
          }
      }
  }
  
  static void
  createDataTreeExampleModule(sr_session_ctx_t *sess)
  {
      const struct ly_ctx *ctx = NULL;
      struct lyd_node *root = NULL;
  
      ctx = sr_get_context(sr_session_get_connection(sess));
  
      const struct lys_module *module = ly_ctx_get_module_implemented(ctx, "example-module");
  
      assert_non_null(module);
  
  #define XPATH "/example-module:container/list[key1='key1'][key2='key2']/leaf"
  
      assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, ctx, XPATH, "Leaf value", 0, &root));
      assert_int_equal(LY_SUCCESS, lyd_validate_all(&root, NULL, LYD_VALIDATE_NO_STATE, NULL));
      assert_int_equal(SR_ERR_OK, sr_replace_config(sess, "example-module", root, 0));
  }
  
  static void
  createDataTreeLargeExampleModule(sr_session_ctx_t *sess, int list_count)
  {
      const struct ly_ctx *ctx = NULL;
      struct lyd_node *root = NULL, *node = NULL;
  
      ctx = sr_get_context(sr_session_get_connection(sess));
  
      const struct lys_module *module = ly_ctx_get_module_implemented(ctx, "example-module");
  
      assert_non_null(module);
  
  #define MAX_XP_LEN 100
      const char *template = "/example-module:container/list[key1='k1%d'][key2='k2%d']/leaf";
      char xpath[MAX_XP_LEN] = {0, };
  
      for (int i = 0; i < list_count; i++) {
          snprintf(xpath, MAX_XP_LEN, template, i, i);
          assert_int_equal(LY_SUCCESS, lyd_new_path(root, ctx, xpath, "Leaf value", 0, &node));
          if (NULL == root) {
              root = node;
          }
      }
      lyd_new_path(root, ctx, "/example-module:container/list[key1='key1'][key2='key2']/leaf", "Leaf value", 0, NULL);
  
      assert_int_equal(LY_SUCCESS, lyd_validate_all(&root, NULL, LYD_VALIDATE_NO_STATE, NULL));
      assert_int_equal(SR_ERR_OK, sr_replace_config(sess, "example-module", root, 0));
  }
  
  static void
  createDataTreeLargeIETFinterfacesModule(sr_session_ctx_t *sess, size_t if_count)
  {
  
      const struct ly_ctx *ctx = sr_get_context(sr_session_get_connection(sess));
  
      struct lyd_node *root = NULL;
  
      #define MAX_IF_LEN 150
      const char *template_prefix_len = "/ietf-interfaces:interfaces/interface[name='eth%d']/ietf-ip:ipv4/ietf-ip:address[ietf-ip:ip='192.168.%d.%d']/ietf-ip:prefix-length";
      const char *template_type = "/ietf-interfaces:interfaces/interface[name='eth%d']/type";
      const char *template_desc = "/ietf-interfaces:interfaces/interface[name='eth%d']/description";
      const char *template_enabled = "/ietf-interfaces:interfaces/interface[name='eth%d']/enabled";
      const char *template_ipv4_enabled = "/ietf-interfaces:interfaces/interface[name='eth%d']/ietf-ip:ipv4/ietf-ip:enabled";
      const char *template_ipv4_mtu = "/ietf-interfaces:interfaces/interface[name='eth%d']/ietf-ip:ipv4/ietf-ip:mtu";
      char xpath[MAX_IF_LEN] = {0, };
  
      const struct lys_module *module_interfaces = ly_ctx_get_module_implemented(ctx, "ietf-interfaces");
      assert_non_null(module_interfaces);
      const struct lys_module *module_ip = ly_ctx_get_module_implemented(ctx, "ietf-ip");
      assert_non_null(module_ip);
      const struct lys_module *module = ly_ctx_get_module(ctx, "iana-if-type", "2014-05-08");
      assert_non_null(module);
      struct lyd_node *node = NULL;
  
      for (size_t i = 1; i < (if_count + 1); i++) {
          snprintf(xpath, MAX_IF_LEN, template_prefix_len, i, (i / 244 + 1), i % 244);
          assert_int_equal(LY_SUCCESS, lyd_new_path(root, ctx, xpath, "24", 0, &node));
          if (NULL == root) {
              root = node;
          }
          snprintf(xpath, MAX_IF_LEN, template_type, i);
          assert_int_equal(LY_SUCCESS, lyd_new_path(root, ctx, xpath, "iana-if-type:ethernetCsmacd", 0, NULL));
  
          snprintf(xpath, MAX_IF_LEN, template_desc, i);
          assert_int_equal(LY_SUCCESS, lyd_new_path(root, ctx, xpath, "ethernet interface", 0, NULL));
  
          snprintf(xpath, MAX_IF_LEN, template_enabled, i);
          assert_int_equal(LY_SUCCESS, lyd_new_path(root, ctx, xpath, "true", 0, NULL));
  
          snprintf(xpath, MAX_IF_LEN, template_ipv4_enabled, i);
          assert_int_equal(LY_SUCCESS, lyd_new_path(root, ctx, xpath, "true", 0, NULL));
  
          snprintf(xpath, MAX_IF_LEN, template_ipv4_mtu, i);
          assert_int_equal(LY_SUCCESS, lyd_new_path(root, ctx, xpath, "1500", 0, NULL));
      }
  
      assert_int_equal(LY_SUCCESS, lyd_validate_all(&root, NULL, LYD_VALIDATE_NO_STATE, NULL));
      assert_int_equal(SR_ERR_OK, sr_replace_config(sess, "ietf-interfaces", root, 0));
  }
  
  int
  main(int argc, char **argv)
  {
      test_t tests[] = {
          {perf_get_item_test, "Get item one leaf", OP_COUNT, sysrepo_setup, sysrepo_teardown},
          {perf_get_item_first_test, "Get item first leaf", OP_COUNT, sysrepo_setup, sysrepo_teardown},
          {perf_get_item_with_data_load_test, "Get item incl session start", OP_COUNT, sysrepo_setup, sysrepo_teardown},
          {perf_get_items_test, "Get items all lists", OP_COUNT, sysrepo_setup, sysrepo_teardown},
          {perf_get_items_iter_test, "Get items iter all lists", OP_COUNT, sysrepo_setup, sysrepo_teardown},
          {perf_get_ietf_intefaces_test, "Get items ietf-if config", OP_COUNT, sysrepo_setup, sysrepo_teardown},
          {perf_get_subtree_test, "Get subtree one leaf", OP_COUNT, sysrepo_setup, sysrepo_teardown},
          {perf_get_subtree_with_data_load_test, "Get subtree incl session start", OP_COUNT, sysrepo_setup, sysrepo_teardown},
          {perf_get_subtrees_test, "Get subtrees all lists", OP_COUNT, sysrepo_setup, sysrepo_teardown},
          {perf_get_ietf_intefaces_tree_test, "Get subtrees ietf-if config", OP_COUNT, sysrepo_setup, sysrepo_teardown},
          {perf_set_delete_test, "Set & delete one list", OP_COUNT, sysrepo_setup, sysrepo_teardown},
          {perf_set_delete_100_test, "Set & delete 100 lists", OP_COUNT_COMMIT, sysrepo_setup, sysrepo_teardown},
          {perf_commit_test, "Commit one leaf change", OP_COUNT_COMMIT, sysrepo_setup, sysrepo_teardown},
          {perf_data_provide_test, "Operational data provide", OP_COUNT_COMMIT, data_provide_setup, data_provide_teardown},
          {perf_rpc_test, "RPC", OP_COUNT_COMMIT, sysrepo_setup, sysrepo_teardown},
          {perf_ev_notification_ephemeral_test, "Event notification - ephemeral", OP_COUNT_COMMIT, sysrepo_setup, sysrepo_teardown},
          {perf_ev_notification_store_test, "Event notification - store", OP_COUNT_COMMIT, sysrepo_setup, sysrepo_teardown},
          {perf_libyang_get_node, "Libyang get one node", OP_COUNT, libyang_setup, libyang_teardown},
          {perf_libyang_get_all_list, "Libyang get all list", OP_COUNT, libyang_setup, libyang_teardown},
      };
  
      size_t test_count = sizeof(tests) / sizeof(*tests);
      sr_conn_ctx_t *conn = NULL;
      sr_session_ctx_t *sess;
      int rc, ret = -1, selection = -1;
      uint32_t conn_count;
  
      if (argc > 1) {
          rc = sscanf(argv[1], "%d", &selection);
          assert_int_equal(rc, 1);
      }
  
      sr_connection_count(&conn_count);
      if (conn_count) {
          puts("There can be no running connections.\n");
          goto cleanup;
      }
  
      sr_connect(0, &conn);
  
      /* install required modules */
      rc = sr_install_module(conn, TESTS_DIR "/files/example-module.yang", TESTS_DIR "/files", NULL);
      if (rc && (rc != SR_ERR_EXISTS)) {
          goto cleanup;
      }
      rc = sr_install_module(conn, TESTS_DIR "/files/referenced-data.yang", TESTS_DIR "/files", NULL);
      if (rc && (rc != SR_ERR_EXISTS)) {
          goto cleanup;
      }
      rc = sr_install_module(conn, TESTS_DIR "/files/test-module.yang", TESTS_DIR "/files", NULL);
      if (rc && (rc != SR_ERR_EXISTS)) {
          goto cleanup;
      }
      rc = sr_install_module(conn, TESTS_DIR "/files/ietf-ip.yang", TESTS_DIR "/files", NULL);
      if (rc && (rc != SR_ERR_EXISTS)) {
          goto cleanup;
      }
      rc = sr_install_module(conn, TESTS_DIR "/files/iana-if-type.yang", TESTS_DIR "/files", NULL);
      if (rc && (rc != SR_ERR_EXISTS)) {
          goto cleanup;
      }
  
      sr_disconnect(conn);
      sr_connect(0, &conn);
      sr_session_start(conn, SR_DS_RUNNING, &sess);
  
      /* one list instance */
      createDataTreeExampleModule(sess);
      createDataTreeLargeIETFinterfacesModule(sess, 1);
      instance_cnt = 1;
      test_perf(tests, test_count, "Data file with one list instance", selection);
  
      /* 20 list instances*/
      createDataTreeLargeExampleModule(sess, 20);
      createDataTreeLargeIETFinterfacesModule(sess, 20);
      instance_cnt = 20;
      test_perf(tests, test_count, "Data file with 20 list instances", selection);
  
      /* decrease the number of performed operation on larger file */
      for (size_t i = 0; i < test_count; i++) {
          if (OP_COUNT_COMMIT != tests[i].op_count) {
              tests[i].op_count = OP_COUNT_LOW;
          }
      }
  
      /* 100 list instances*/
      createDataTreeLargeExampleModule(sess, 100);
      createDataTreeLargeIETFinterfacesModule(sess, 100);
      instance_cnt = 100;
      test_perf(tests, test_count, "Data file with 100 list instances", selection);
      puts("\n\n");
      ret = 0;
  
  cleanup:
      /* uninstall modules */
      sr_remove_module(conn, "example-module");
      sr_remove_module(conn, "test-module");
      sr_remove_module(conn, "referenced-data");
      sr_remove_module(conn, "ietf-ip");
      sr_remove_module(conn, "iana-if-type");
      sr_remove_module(conn, "ietf-interfaces");
      sr_disconnect(conn);
      return ret;
  }
  