Skip to content

Commit

Permalink
Implemented enhancement CiscoDevNet#925 in C++
Browse files Browse the repository at this point in the history
  • Loading branch information
ygorelik committed Jul 3, 2019
1 parent facc095 commit c4c56b2
Show file tree
Hide file tree
Showing 7 changed files with 386 additions and 8 deletions.
124 changes: 123 additions & 1 deletion sdk/cpp/core/src/entity_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
//////////////////////////////////////////////////////////////////

#include <algorithm>
#include <vector>

#include "entity_util.hpp"
#include "errors.hpp"
Expand Down Expand Up @@ -129,4 +128,127 @@ const EntityPath get_entity_path(const Entity & entity, Entity* ancestor)
return get_entity_path(entity, path_buffer.str());
}

std::string absolute_path(Entity & entity)
{
string path = entity.get_segment_path();
if (!entity.is_top_level_class && entity.parent)
{
path = absolute_path(*entity.parent) + "/" + path;
}
return path;
}

std::map<std::string,std::string> entity_to_dict(Entity & entity)
{
std::map<std::string,std::string> edict{};
auto abs_path = absolute_path(entity);
if (entity.is_presence_container || abs_path.rfind("]") == abs_path.length()-1)
{
edict[abs_path] = "";
}
auto name_leaf_data_vector = entity.get_name_leaf_data();
for (auto name_leaf_data : name_leaf_data_vector)
{
auto leaf_name = name_leaf_data.first;
auto leaf_value = name_leaf_data.second.value;
std::ostringstream key_buffer;
key_buffer << "[" << leaf_name << "=";
if (abs_path.find(key_buffer.str()) == string::npos)
{
std::string path = abs_path + "/" + leaf_name;
edict[path] = leaf_value;
}
}
for (auto const & entry : entity.get_children())
{
auto child = entry.second;
auto child_dict = entity_to_dict(*child);
for (auto const & e : child_dict)
{
edict[e.first] = e.second;
}
}
return edict;
}

static bool key_in_vector(string & k, vector<string> v)
{
for (auto e : v)
{
if (e == k) {
return true;
}
}
return false;
}

static void remove_key_from_vector(string & k, vector<string> & v)
{
for (vector<string>::iterator it=v.begin(); it!=v.end(); it++)
{
if (k == *it)
{
v.erase(it);
break;
}
}
}

std::map<std::string, std::pair<std::string,std::string>> entity_diff(Entity & ent1, Entity & ent2)
{
if (typeid(ent1) != typeid(ent2))
{
throw(YInvalidArgumentError{"entity_diff: Incompatible arguments provided."});
}
std::map<std::string, std::pair<std::string,std::string>> diffs{};
auto ent1_dict = entity_to_dict(ent1);
auto ent2_dict = entity_to_dict(ent2);
vector<string> ent1_keys;
for (auto entry : ent1_dict) ent1_keys.push_back(entry.first);
vector<string> ent2_keys;
for (auto entry : ent2_dict) ent2_keys.push_back(entry.first);
vector<string> ent1_skip_keys;
for (auto key : ent1_keys)
{
if (key_in_vector(key, ent1_skip_keys))
continue;
if (key_in_vector(key, ent2_keys))
{
if (ent1_dict[key] != ent2_dict[key])
{
diffs[key] = make_pair(ent1_dict[key], ent2_dict[key]);
}
remove_key_from_vector(key, ent2_keys);
}
else
{
diffs[key] = make_pair(ent1_dict[key], "None");
for (auto dup_key : ent1_keys)
{
if (dup_key.find(key) == 0)
{
ent1_skip_keys.push_back(dup_key);
}
}
}
}
vector<string> ent2_skip_keys;
for (auto key : ent2_keys)
{
if (key_in_vector(key, ent2_skip_keys))
continue;
diffs[key] = make_pair("None", ent2_dict[key]);
for (auto dup_key : ent2_keys)
{
if (dup_key.find(key) == 0)
{
ent2_skip_keys.push_back(dup_key);
}
}
}
return diffs;
}



}
6 changes: 6 additions & 0 deletions sdk/cpp/core/src/entity_util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ bool is_set(const YFilter & yfilter);

const EntityPath get_entity_path(const Entity & entity, Entity* ancestor);

std::string absolute_path(Entity & entity);

std::map<std::string,std::string> entity_to_dict(Entity & entity);

std::map<std::string, std::pair<std::string,std::string>> entity_diff(Entity & ent1, Entity & ent2);

}

#define ADD_KEY_TOKEN(attr, attr_name) {\
Expand Down
1 change: 1 addition & 0 deletions sdk/cpp/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ set(bundle_tests_src
main.cpp
test_c_api.cpp
test_crud.cpp
test_entity_diff.cpp
test_errors.cpp
test_executor_service.cpp
test_netconf_operations.cpp
Expand Down
239 changes: 239 additions & 0 deletions sdk/cpp/tests/test_entity_diff.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
/// YANG Development Kit
// Copyright 2019 Cisco Systems. All rights reserved
//
////////////////////////////////////////////////////////////////
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
//////////////////////////////////////////////////////////////////

#include <iostream>
#include <spdlog/spdlog.h>

#include <ydk/entity_util.hpp>

#include <ydk_ydktest/ydktest_sanity.hpp>

#include "config.hpp"
#include "catch.hpp"
#include "test_utils.hpp"

using namespace std;
using namespace ydk;
using namespace ydktest;

string check_empty_str_value(string & v)
{
if (v.empty())
v = "exists";
return v;
}

void print_dictionary(const string & legend, map<string,string> & ent_dict)
{
cout << "\n------> DICTIONARY" << legend << endl;
for (map<string,string>::iterator it = ent_dict.begin(); it != ent_dict.end(); it++)
{
cout << it->first << ": " << check_empty_str_value(it->second) << endl;
}
}

void print_diffs(map<string, pair<string,string>> & diff)
{
cout << "\n------> DIFFS:" << endl;
for (auto const & entry : diff)
{
auto value_pair = entry.second;
cout << entry.first << ": " << check_empty_str_value(value_pair.first) << " vs "
<< check_empty_str_value(value_pair.second) << endl;
}
}

TEST_CASE( "test_entity_diff_two_key" )
{
auto runner1 = ydktest_sanity::Runner{};
auto l_1 = make_shared<ydktest_sanity::Runner::TwoKeyList>();
l_1->first = "f1";
l_1->second = 11;
l_1->property = "82";
auto l_2 = make_shared<ydktest_sanity::Runner::TwoKeyList>();
l_2->first = "f2";
l_2->second = 22;
l_2->property = "83";
runner1.two_key_list.extend({l_1, l_2});

auto ent_dict1 = entity_to_dict(runner1);
REQUIRE(ent_dict1.size() == 4);
print_dictionary("-LEFT", ent_dict1);

auto runner2 = ydktest_sanity::Runner{};
l_1 = make_shared<ydktest_sanity::Runner::TwoKeyList>();
l_1->first = "f1";
l_1->second = 11;
l_1->property = "82";
l_2 = make_shared<ydktest_sanity::Runner::TwoKeyList>();
l_2->first = "f2";
l_2->second = 22;
l_2->property = "83";
runner2.two_key_list.extend({l_1, l_2});

auto diff = entity_diff(runner1, runner2);
REQUIRE(diff.size() == 0);

l_1->property = "83";
auto ent_dict2 = entity_to_dict(runner2);
print_dictionary("-RIGHT", ent_dict2);
diff = entity_diff(runner1, runner2);
REQUIRE(diff.size() == 1);
print_diffs(diff);
}

TEST_CASE( "test_entity_diff_two_key_not_equal" )
{
auto runner1 = ydktest_sanity::Runner{};
auto l_1 = make_shared<ydktest_sanity::Runner::TwoKeyList>();
l_1->first = "f1";
l_1->second = 11;
l_1->property = "82";
auto l_2 = make_shared<ydktest_sanity::Runner::TwoKeyList>();
l_2->first = "f2";
l_2->second = 22;
l_2->property = "83";
runner1.two_key_list.extend({l_1, l_2});

auto ent_dict1 = entity_to_dict(runner1);
REQUIRE(ent_dict1.size() == 4);
print_dictionary("-LEFT", ent_dict1);

auto runner2 = ydktest_sanity::Runner{};
l_1 = make_shared<ydktest_sanity::Runner::TwoKeyList>();
l_1->first = "f1";
l_1->second = 11;
l_1->property = "82";
l_2 = make_shared<ydktest_sanity::Runner::TwoKeyList>();
l_2->first = "f3";
l_2->second = 22;
l_2->property = "83";
runner2.two_key_list.extend({l_1, l_2});

auto ent_dict2 = entity_to_dict(runner2);
print_dictionary("-RIGHT", ent_dict2);

auto diff = entity_diff(runner1, runner2);
REQUIRE(diff.size() == 2);
print_diffs(diff);
}

TEST_CASE( "test_entity_to_dict_aug_onelist" )
{
auto runner = ydktest_sanity::Runner{};
auto e_1 = make_shared<ydktest_sanity::Runner::OneList::OneAugList::Ldata>();
auto e_2 = make_shared<ydktest_sanity::Runner::OneList::OneAugList::Ldata>();
e_1->number = 1;
e_1->name = "e_1.name";
e_2->number = 2;
e_2->name = "e_2.name";
runner.one_list->one_aug_list->ldata.extend({e_1, e_2});
runner.one_list->one_aug_list->enabled = true;

auto ent_dict = entity_to_dict(runner);
REQUIRE(ent_dict.size() == 5);
print_dictionary("", ent_dict);
}

TEST_CASE( "test_entity_to_dict_enum_leaflist" )
{
auto runner = ydktest_sanity::Runner{};
runner.ytypes->built_in_t->enum_llist.append(ydktest_sanity::YdkEnumTest::local);
runner.ytypes->built_in_t->enum_llist.append(ydktest_sanity::YdkEnumTest::remote);

auto ent_dict = entity_to_dict(runner);
REQUIRE(ent_dict.size() == 2);
print_dictionary("", ent_dict);
}

TEST_CASE( "test_entity_diff_presence" )
{
auto runner = ydktest_sanity::Runner();
runner.runner_2 = make_shared<ydktest_sanity::Runner::Runner2>();
runner.runner_2->some_leaf = "some-leaf";

auto ent_dict = entity_to_dict(runner);
REQUIRE(ent_dict.size() == 2);
print_dictionary("-LEFT", ent_dict);

auto empty_runner = ydktest_sanity::Runner();
ent_dict = entity_to_dict(empty_runner);
REQUIRE(ent_dict.size() == 0);
print_dictionary("-RIGHT", ent_dict);

auto diff = entity_diff(runner, empty_runner);
REQUIRE(diff.size() == 1);
print_diffs(diff);
}

TEST_CASE( "test_entity_diff_two_list_pos" )
{
auto r_1 = ydktest_sanity::Runner();
auto e_1 = make_shared<ydktest_sanity::Runner::TwoList::Ldata>();
auto e_2 = make_shared<ydktest_sanity::Runner::TwoList::Ldata>();
auto e_11 = make_shared<ydktest_sanity::Runner::TwoList::Ldata::Subl1>();
auto e_12 = make_shared<ydktest_sanity::Runner::TwoList::Ldata::Subl1>();
e_1->number = 21;
e_1->name = "runner:twolist:ldata[21]:name";
e_11->number = 211;
e_11->name = "runner:twolist:ldata[21]:subl1[211]:name";
e_12->number = 212;
e_12->name = "runner:twolist:ldata[21]:subl1[212]:name";
e_1->subl1.extend({e_11, e_12});
auto e_21 = make_shared<ydktest_sanity::Runner::TwoList::Ldata::Subl1>();
auto e_22 = make_shared<ydktest_sanity::Runner::TwoList::Ldata::Subl1>();
e_2->number = 22;
e_2->name = "runner:twolist:ldata[22]:name";
e_21->number = 221;
e_21->name = "runner:twolist:ldata[22]:subl1[221]:name";
e_22->number = 222;
e_22->name = "runner:twolist:ldata[22]:subl1[222]:name";
e_2->subl1.extend({e_21, e_22});
r_1.two_list->ldata.extend({e_1, e_2});

auto ent_dict = entity_to_dict(r_1);
REQUIRE(ent_dict.size() == 12);
print_dictionary("-LEFT", ent_dict);

auto r_2 = ydktest_sanity::Runner();
e_1 = make_shared<ydktest_sanity::Runner::TwoList::Ldata>();
e_2 = make_shared<ydktest_sanity::Runner::TwoList::Ldata>();
e_11 = make_shared<ydktest_sanity::Runner::TwoList::Ldata::Subl1>();
e_12 = make_shared<ydktest_sanity::Runner::TwoList::Ldata::Subl1>();
e_1->number = 21;
e_1->name = "runner:twolist:ldata[21]:name";
e_11->number = 211;
e_11->name = "runner:twolist:ldata[21]:subl1[211]:name";
e_12->number = 212;
e_12->name = "runner:twolist:ldata[21]:subl1[212]:name";
e_1->subl1.extend({e_11, e_12});
r_2.two_list->ldata.append(e_1);

ent_dict = entity_to_dict(r_2);
REQUIRE(ent_dict.size() == 6);
print_dictionary("-RIGHT", ent_dict);

auto diff = entity_diff(r_1, r_2);
REQUIRE(diff.size() == 1);
print_diffs(diff);
}
Loading

0 comments on commit c4c56b2

Please sign in to comment.