From 0bde17c8e1ebac2cab3d91f3dbacba9b7c259232 Mon Sep 17 00:00:00 2001 From: Shuotian Cheng Date: Mon, 29 Aug 2016 14:57:51 -0700 Subject: [PATCH] producertable: Adding dump file to record producer activities to file - Use json.hpp to construct the JSON data and store into the m_dumpFile, which could be used as input file for swssconfig. - Add unit test for verification --- common/json.h | 4 +-- common/producertable.cpp | 51 ++++++++++++++++++++++++++--- common/producertable.h | 7 ++++ tests/Makefile.am | 3 +- tests/json_ut.cpp | 71 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 128 insertions(+), 8 deletions(-) create mode 100644 tests/json_ut.cpp diff --git a/common/json.h b/common/json.h index cab880b64..ecd5d1520 100644 --- a/common/json.h +++ b/common/json.h @@ -8,10 +8,10 @@ namespace swss { -class JSon { +class JSon +{ public: static std::string buildJson(const std::vector &fv); - static void readJson(const std::string &json, std::vector &fv); }; diff --git a/common/producertable.cpp b/common/producertable.cpp index 3af7114a1..fd2a1b25a 100644 --- a/common/producertable.cpp +++ b/common/producertable.cpp @@ -1,10 +1,12 @@ #include "common/redisreply.h" #include "common/producertable.h" #include "common/json.h" +#include "common/json.hpp" #include #include using namespace std; +using json = nlohmann::json; namespace swss { @@ -13,6 +15,21 @@ ProducerTable::ProducerTable(DBConnector *db, string tableName) : { } +ProducerTable::ProducerTable(DBConnector *db, string tableName, string dumpFile) : + Table(db, tableName) +{ + m_dumpFile.open(dumpFile, fstream::out | fstream::trunc); + m_dumpFile << "[" << endl; +} + +ProducerTable::~ProducerTable() { + if (m_dumpFile.is_open()) + { + m_dumpFile << endl << "]" << endl; + m_dumpFile.close(); + } +} + void ProducerTable::enqueueDbChange(string key, string value, string op) { string lpush_value; @@ -44,21 +61,45 @@ void ProducerTable::enqueueDbChange(string key, string value, string op) void ProducerTable::set(string key, vector &values, string op) { - multi(); + if (m_dumpFile.is_open()) + { + if (!m_firstItem) + m_dumpFile << "," << endl; + else + m_firstItem = false; + + json j; + string json_key = getKeyName(key); + j[json_key] = json::object(); + for (auto it : values) + j[json_key][fvField(it)] = fvValue(it); + j["OP"] = op; + m_dumpFile << j.dump(4); + } + multi(); enqueueDbChange(key, JSon::buildJson(values), "S" + op); exec(); } void ProducerTable::del(string key, string op) { - string del("DEL "); - del += getKeyName(key); + if (m_dumpFile.is_open()) + { + if (!m_firstItem) + m_dumpFile << "," << endl; + else + m_firstItem = false; - multi(); + json j; + string json_key = getKeyName(key); + j[json_key] = json::object(); + j["OP"] = op; + m_dumpFile << j.dump(4); + } + multi(); enqueueDbChange(key, "{}", "D" + op); - exec(); } diff --git a/common/producertable.h b/common/producertable.h index 556737430..36f1d3698 100644 --- a/common/producertable.h +++ b/common/producertable.h @@ -1,6 +1,8 @@ #ifndef __PRODUCERTABLE__ #define __PRODUCERTABLE__ +#include +#include #include #include @@ -14,6 +16,8 @@ class ProducerTable : public Table { public: ProducerTable(DBConnector *db, std::string tableName); + ProducerTable(DBConnector *db, std::string tableName, std::string dumpFile); + ~ProducerTable(); /* Implements set() and del() commands using notification messages */ virtual void set(std::string key, std::vector &values, @@ -25,6 +29,9 @@ class ProducerTable : public Table ProducerTable(const ProducerTable &other); ProducerTable & operator = (const ProducerTable &other); + std::ofstream m_dumpFile; + bool m_firstItem = true; + void enqueueDbChange(std::string key, std::string value, std::string op); }; diff --git a/tests/Makefile.am b/tests/Makefile.am index ab7b36730..85bb51301 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -13,7 +13,8 @@ LDADD_GTEST = $(top_srcdir)/googletest/build/googlemock/gtest/libgtest_main.a \ $(top_srcdir)/googletest/build/googlemock/gtest/libgtest.a tests_SOURCES = redis_ut.cpp \ - tokenize_ut.cpp + tokenize_ut.cpp \ + json_ut.cpp tests_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_GTEST) tests_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_GTEST) diff --git a/tests/json_ut.cpp b/tests/json_ut.cpp new file mode 100644 index 000000000..fc2c6dd1b --- /dev/null +++ b/tests/json_ut.cpp @@ -0,0 +1,71 @@ +#include "common/json.hpp" +#include "common/producertable.h" +#include "gtest/gtest.h" + +using namespace std; +using namespace swss; +using json = nlohmann::json; + +#define TEST_VIEW (7) +#define TEST_DUMP_FILE "ut_dump_file.txt" + +TEST(JSON, test) +{ + /* Construct the file */ + DBConnector db(TEST_VIEW, "localhost", 6379, 0); + ProducerTable *p; + p = new ProducerTable(&db, "UT_REDIS", TEST_DUMP_FILE); + + vector fvTuples; + FieldValueTuple fv1("test_field_1", "test_value_1"); + fvTuples.push_back(fv1); + + p->set("test_key_1", fvTuples); + + FieldValueTuple fv2("test_field_2", "test_value_2"); + fvTuples.push_back(fv2); + + p->set("test_key_2", fvTuples); + + p->del("test_key_1"); + + delete(p); + + /* Read the file and validate the content */ + fstream file(TEST_DUMP_FILE, fstream::in); + json j; + file >> j; + + EXPECT_TRUE(j.is_array()); + EXPECT_TRUE(j.size() == 3); + + for (size_t i = 0; i < j.size(); i++) + { + auto item = j[i]; + EXPECT_TRUE(item.is_object()); + EXPECT_TRUE(item.size() == 2); + + for (auto it = item.begin(); it != item.end(); it++) + { + if (it.key() == "OP") + EXPECT_TRUE(it.value() == "SET" || it.value() == "DEL"); + else + { + EXPECT_TRUE(it.key() == "UT_REDIS:test_key_1" || it.key() == "UT_REDIS:test_key_2"); + auto subitem = it.value(); + EXPECT_TRUE(subitem.is_object()); + if (subitem.size() > 0) + { + for (auto subit = subitem.begin(); subit != subitem.end(); subit++) + { + if (subit.key() == "test_field_1") + EXPECT_EQ(subit.value(), "test_value_1"); + if (subit.key() == "test_field_2") + EXPECT_EQ(subit.value(), "test_value_2"); + } + } + } + } + } + file.close(); +}