Commit 366177a3 authored by Jelte Jansen's avatar Jelte Jansen
Browse files

added tests for ModuleCCSession class (with a fake Session object)

added a few function declarations in ccsession.h that were missing (but were already implemented in ccsession.cc)
fixed 1 bug in ccsession.cc, other ones are pending (tests already exist but commented out right now)


git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@1359 e5f2f494-b856-4b98-b285-d166d9295462
parent 75727c97
......@@ -213,6 +213,7 @@ ModuleCCSession::init(
) throw (isc::cc::SessionError)
{
module_specification_ = readModuleSpecification(spec_file_name);
setModuleSpec(module_specification_);
sleep(1);
module_name_ = module_specification_.getFullSpec()->get("module_name")->stringValue();
......@@ -239,7 +240,7 @@ ModuleCCSession::init(
std::cerr << "[" << module_name_ << "] Error in specification: " << answer << std::endl;
}
config_ = Element::createFromString("{}");
setLocalConfig(Element::createFromString("{}"));
// get any stored configuration from the manager
if (config_handler_) {
ElementPtr cmd = Element::createFromString("{ \"command\": [\"get_config\", {\"module_name\":\"" + module_name_ + "\"} ] }");
......@@ -279,7 +280,9 @@ ModuleCCSession::handleConfigUpdate(ElementPtr new_config)
int rcode;
parseAnswer(rcode, answer);
if (rcode == 0) {
isc::data::merge(config_, new_config);
ElementPtr local_config = getLocalConfig();
isc::data::merge(local_config, new_config);
setLocalConfig(local_config);
}
}
return answer;
......
......@@ -33,6 +33,15 @@ class io_service;
namespace isc {
namespace config {
ElementPtr createAnswer(const int rcode);
ElementPtr createAnswer(const int rcode, const ElementPtr arg);
ElementPtr createAnswer(const int rcode, const std::string& arg);
ElementPtr parseAnswer(int &rcode, const ElementPtr msg);
ElementPtr createCommand(const std::string& command, ElementPtr arg);
const std::string parseCommand(ElementPtr& arg, const ElementPtr command);
///
/// \brief A standard cc session exception that is thrown if a function
/// is there is a problem with one of the messages
......@@ -153,7 +162,6 @@ private:
std::string module_name_;
isc::cc::Session session_;
ModuleSpec module_specification_;
isc::data::ElementPtr config_;
ElementPtr handleConfigUpdate(ElementPtr new_config);
isc::data::ElementPtr(*config_handler_)(isc::data::ElementPtr new_config);
......@@ -163,10 +171,6 @@ private:
void updateRemoteConfig(const std::string& module_name, ElementPtr new_config);
};
ElementPtr createAnswer(const int rcode);
ElementPtr createAnswer(const int rcode, const ElementPtr arg);
ElementPtr createAnswer(const int rcode, const std::string& arg);
}
}
#endif // __CCSESSION_H
......
......@@ -2,15 +2,22 @@ AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/ext
CLEANFILES = *.gcno *.gcda
lib_LTLIBRARIES = libfake_session.la
libfake_session_la_SOURCES = fake_session.h fake_session.cc
TESTS =
if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = module_spec_unittests.cc config_data_unittests.cc run_unittests.cc
#run_unittests_SOURCES = ccsession_unittests.cc module_spec_unittests.cc config_data_unittests.cc run_unittests.cc
run_unittests_SOURCES = ccsession_unittests.cc run_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(top_builddir)/src/lib/config/libcfgclient.la $(GTEST_LDADD)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.a
run_unittests_LDADD += $(top_builddir)/src/lib/config/libcfgclient.la
run_unittests_LDADD += $(top_builddir)/src/lib/cc/data.o
run_unittests_LDADD += libfake_session.la
if HAVE_BOOSTLIB
run_unittests_LDFLAGS += $(AM_LDFLAGS) $(BOOST_LDFLAGS)
......
// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
// $Id: module_spec_unittests.cc 1321 2010-03-11 10:17:03Z jelte $
#include <gtest/gtest.h>
#include "fake_session.h"
#include <config/ccsession.h>
#include <fstream>
#include "data_def_unittests_config.h"
using namespace isc::data;
using namespace isc::config;
using namespace std;
std::string ccspecfile(const std::string name) {
return std::string(TEST_DATA_PATH) + "/" + name;
}
static ElementPtr
el(const std::string& str)
{
return Element::createFromString(str);
}
// upon creation of a ModuleCCSession, the class
// sends its specification to the config manager
// it expects an ok answer back, so everytime we
// create a ModuleCCSession, we must set an initial
// ok answer
void
initFakeSession()
{
initial_messages = el("[]");
msg_queue = el("[]");
subscriptions = el("[]");
initial_messages->add(createAnswer(0));
}
void
endFakeSession()
{
initial_messages = ElementPtr();
msg_queue = ElementPtr();
subscriptions = ElementPtr();
}
TEST(CCSession, createAnswer)
{
ElementPtr answer;
// todo: this should fail (actually int rcode should probably be removed)
answer = createAnswer(1);
answer = createAnswer(0);
EXPECT_EQ("{\"result\": [ 0 ]}", answer->str());
answer = createAnswer(1, "error");
EXPECT_EQ("{\"result\": [ 1, \"error\" ]}", answer->str());
// todo: this should fail
answer = createAnswer(0, "error");
ElementPtr arg = el("[ \"just\", \"some\", \"data\" ]");
answer = createAnswer(0, arg);
EXPECT_EQ("{\"result\": [ 0, [ \"just\", \"some\", \"data\" ] ]}", answer->str());
}
TEST(CCSession, parseAnswer)
{
ElementPtr answer;
ElementPtr arg;
int rcode;
// todo: these should throw CCSessionError
//answer = el("1");
//arg = parseAnswer(rcode, ElementPtr());
//arg = parseAnswer(rcode, answer);
// etc
answer = el("{\"result\": [ 0 ]}");
arg = parseAnswer(rcode, answer);
EXPECT_EQ(0, rcode);
EXPECT_TRUE(isNull(arg));
// todo: this should throw
answer = el("{\"result\": [ 1 ]}");
answer = el("{\"result\": [ 1, \"error\"]}");
arg = parseAnswer(rcode, answer);
EXPECT_EQ(1, rcode);
EXPECT_EQ("error", arg->stringValue());
answer = el("{\"result\": [ 0, [ \"just\", \"some\", \"data\" ] ] }");
arg = parseAnswer(rcode, answer);
EXPECT_EQ(0, rcode);
EXPECT_EQ("[ \"just\", \"some\", \"data\" ]", arg->str());
}
TEST(CCSession, createCommand)
{
ElementPtr command;
ElementPtr arg;
// todo: add overload for no arg
//command = createCommand("my_command");
//ASSERT_EQ("{\"command\": [ \"my_command\" ]", command->str());
arg = el("1");
command = createCommand("my_command", arg);
ASSERT_EQ("{\"command\": [ \"my_command\", 1 ]}", command->str());
arg = el("[ \"a\", \"b\" ]");
command = createCommand("my_cmd", arg);
ASSERT_EQ("{\"command\": [ \"my_cmd\", [ \"a\", \"b\" ] ]}", command->str());
arg = el("{ \"a\": \"map\" }");
command = createCommand("foo", arg);
ASSERT_EQ("{\"command\": [ \"foo\", {\"a\": \"map\"} ]}", command->str());
}
TEST(CCSession, parseCommand)
{
ElementPtr arg;
std::string cmd;
// should throw
//parseCommand(arg, ElementPtr());
parseCommand(arg, el("1"));
parseCommand(arg, el("{}"));
parseCommand(arg, el("{\"not a command\": 1 }"));
parseCommand(arg, el("{\"command\": 1 }"));
parseCommand(arg, el("{\"command\": [] }"));
parseCommand(arg, el("{\"command\": [ 1 ] }"));
parseCommand(arg, el("{\"command\": [ \"command\" ] }"));
cmd = parseCommand(arg, el("{\"command\": [ \"my_command\", 1 ] }"));
EXPECT_EQ("my_command", cmd);
EXPECT_EQ("1", arg->str());
parseCommand(arg, el("{\"command\": [ \"my_command\", [ \"some\", \"argument\", \"list\" ] ] }"));
EXPECT_EQ("my_command", cmd);
EXPECT_EQ("[ \"some\", \"argument\", \"list\" ]", arg->str());
}
TEST(CCSession, session1)
{
initFakeSession();
EXPECT_EQ(false, haveSubscription("Spec1", "*"));
ModuleCCSession mccs(ccspecfile("spec1.spec"), NULL, NULL);
EXPECT_EQ(true, haveSubscription("Spec1", "*"));
EXPECT_EQ(1, msg_queue->size());
ElementPtr msg;
std::string group, to;
msg = getFirstMessage(group, to);
EXPECT_EQ("{\"command\": [ \"module_spec\", {\"module_name\": \"Spec1\"} ]}", msg->str());
EXPECT_EQ("ConfigManager", group);
EXPECT_EQ("*", to);
EXPECT_EQ(0, msg_queue->size());
endFakeSession();
}
TEST(CCSession, session2)
{
initFakeSession();
EXPECT_EQ(false, haveSubscription("Spec2", "*"));
ModuleCCSession mccs(ccspecfile("spec2.spec"), NULL, NULL);
EXPECT_EQ(true, haveSubscription("Spec2", "*"));
EXPECT_EQ(1, msg_queue->size());
ElementPtr msg;
std::string group, to;
msg = getFirstMessage(group, to);
EXPECT_EQ("{\"command\": [ \"module_spec\", {\"commands\": [ {\"command_args\": [ {\"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": False, \"item_type\": \"string\"} ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\"}, {\"command_args\": [ ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\"} ], \"config_data\": [ {\"item_default\": 1, \"item_name\": \"item1\", \"item_optional\": False, \"item_type\": \"integer\"}, {\"item_default\": 1.1, \"item_name\": \"item2\", \"item_optional\": False, \"item_type\": \"real\"}, {\"item_default\": True, \"item_name\": \"item3\", \"item_optional\": False, \"item_type\": \"boolean\"}, {\"item_default\": \"test\", \"item_name\": \"item4\", \"item_optional\": False, \"item_type\": \"string\"}, {\"item_default\": [ \"a\", \"b\" ], \"item_name\": \"item5\", \"item_optional\": False, \"item_type\": \"list\", \"list_item_spec\": {\"item_default\": \"\", \"item_name\": \"list_element\", \"item_optional\": False, \"item_type\": \"string\"}}, {\"item_default\": {}, \"item_name\": \"item6\", \"item_optional\": False, \"item_type\": \"map\", \"map_item_spec\": [ {\"item_default\": \"default\", \"item_name\": \"value1\", \"item_optional\": True, \"item_type\": \"string\"}, {\"item_name\": \"value2\", \"item_optional\": True, \"item_type\": \"integer\"} ]} ], \"module_name\": \"Spec2\"} ]}", msg->str());
EXPECT_EQ("ConfigManager", group);
EXPECT_EQ("*", to);
EXPECT_EQ(0, msg_queue->size());
endFakeSession();
}
ElementPtr my_config_handler(ElementPtr new_config)
{
if (new_config && new_config->contains("item1") &&
new_config->get("item1")->intValue() == 5) {
return createAnswer(6, "I do not like the number 5");
}
return createAnswer(0);
}
ElementPtr my_command_handler(const std::string& command, ElementPtr arg UNUSED_PARAM)
{
if (command == "good_command") {
return createAnswer(0);
} else if (command == "command_with_arg") {
if (arg) {
if (arg->getType() == Element::integer) {
return createAnswer(0, el("2"));
} else {
return createAnswer(1, "arg bad type");
}
} else {
return createAnswer(1, "arg missing");
}
} else {
return createAnswer(1, "bad command");
}
}
TEST(CCSession, session3)
{
initFakeSession();
// client will ask for config
initial_messages->add(createAnswer(0, el("{}")));
EXPECT_EQ(false, haveSubscription("Spec2", "*"));
ModuleCCSession mccs(ccspecfile("spec2.spec"), my_config_handler, my_command_handler);
EXPECT_EQ(true, haveSubscription("Spec2", "*"));
EXPECT_EQ(2, msg_queue->size());
ElementPtr msg;
std::string group, to;
msg = getFirstMessage(group, to);
EXPECT_EQ("{\"command\": [ \"module_spec\", {\"commands\": [ {\"command_args\": [ {\"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": False, \"item_type\": \"string\"} ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\"}, {\"command_args\": [ ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\"} ], \"config_data\": [ {\"item_default\": 1, \"item_name\": \"item1\", \"item_optional\": False, \"item_type\": \"integer\"}, {\"item_default\": 1.1, \"item_name\": \"item2\", \"item_optional\": False, \"item_type\": \"real\"}, {\"item_default\": True, \"item_name\": \"item3\", \"item_optional\": False, \"item_type\": \"boolean\"}, {\"item_default\": \"test\", \"item_name\": \"item4\", \"item_optional\": False, \"item_type\": \"string\"}, {\"item_default\": [ \"a\", \"b\" ], \"item_name\": \"item5\", \"item_optional\": False, \"item_type\": \"list\", \"list_item_spec\": {\"item_default\": \"\", \"item_name\": \"list_element\", \"item_optional\": False, \"item_type\": \"string\"}}, {\"item_default\": {}, \"item_name\": \"item6\", \"item_optional\": False, \"item_type\": \"map\", \"map_item_spec\": [ {\"item_default\": \"default\", \"item_name\": \"value1\", \"item_optional\": True, \"item_type\": \"string\"}, {\"item_name\": \"value2\", \"item_optional\": True, \"item_type\": \"integer\"} ]} ], \"module_name\": \"Spec2\"} ]}", msg->str());
EXPECT_EQ("ConfigManager", group);
EXPECT_EQ("*", to);
EXPECT_EQ(1, msg_queue->size());
msg = getFirstMessage(group, to);
EXPECT_EQ("{\"command\": [ \"get_config\", {\"module_name\": \"Spec2\"} ]}", msg->str());
EXPECT_EQ("ConfigManager", group);
EXPECT_EQ("*", to);
EXPECT_EQ(0, msg_queue->size());
endFakeSession();
}
TEST(CCSession, checkCommand)
{
initFakeSession();
// client will ask for config
initial_messages->add(createAnswer(0, el("{}")));
EXPECT_EQ(false, haveSubscription("Spec2", "*"));
ModuleCCSession mccs(ccspecfile("spec2.spec"), my_config_handler, my_command_handler);
EXPECT_EQ(true, haveSubscription("Spec2", "*"));
EXPECT_EQ(2, msg_queue->size());
ElementPtr msg;
std::string group, to;
// checked above, drop em
msg = getFirstMessage(group, to);
msg = getFirstMessage(group, to);
int result;
result = mccs.checkCommand();
EXPECT_EQ(0, result);
// todo: type check in message handling in checkCommand
//addMessage(el("1"), "Spec2", "*");
//result = mccs.checkCommand();
//EXPECT_EQ(0, result);
addMessage(el("{ \"command\": [ \"good_command\" ] }"), "Spec2", "*");
result = mccs.checkCommand();
EXPECT_EQ(1, msg_queue->size());
msg = getFirstMessage(group, to);
EXPECT_EQ("{\"result\": [ 0 ]}", msg->str());
EXPECT_EQ(0, result);
addMessage(el("{ \"command\": \"bad_command\" }"), "Spec2", "*");
result = mccs.checkCommand();
EXPECT_EQ(1, msg_queue->size());
msg = getFirstMessage(group, to);
EXPECT_EQ("{\"result\": [ 1, \"bad command\" ]}", msg->str());
EXPECT_EQ(0, result);
addMessage(el("{ \"command\": [ \"command_with_arg\", 1 ] }"), "Spec2", "*");
result = mccs.checkCommand();
EXPECT_EQ(1, msg_queue->size());
msg = getFirstMessage(group, to);
EXPECT_EQ("{\"result\": [ 0, 2 ]}", msg->str());
EXPECT_EQ(0, result);
addMessage(el("{ \"command\": [ \"command_with_arg\" ] }"), "Spec2", "*");
result = mccs.checkCommand();
EXPECT_EQ(1, msg_queue->size());
msg = getFirstMessage(group, to);
EXPECT_EQ("{\"result\": [ 1, \"arg missing\" ]}", msg->str());
EXPECT_EQ(0, result);
addMessage(el("{ \"command\": [ \"command_with_arg\", \"asdf\" ] }"), "Spec2", "*");
result = mccs.checkCommand();
EXPECT_EQ(1, msg_queue->size());
msg = getFirstMessage(group, to);
EXPECT_EQ("{\"result\": [ 1, \"arg bad type\" ]}", msg->str());
EXPECT_EQ(0, result);
mccs.setCommandHandler(NULL);
addMessage(el("{ \"command\": [ \"whatever\" ] }"), "Spec2", "*");
result = mccs.checkCommand();
EXPECT_EQ(1, msg_queue->size());
msg = getFirstMessage(group, to);
EXPECT_EQ("{\"result\": [ 1, \"Command given but no command handler for module\" ]}", msg->str());
EXPECT_EQ(0, result);
EXPECT_EQ(1, mccs.getValue("item1")->intValue());
addMessage(el("{ \"command\": [ \"config_update\", { \"item1\": 2 } ] }"), "Spec2", "*");
result = mccs.checkCommand();
EXPECT_EQ(1, msg_queue->size());
msg = getFirstMessage(group, to);
EXPECT_EQ("{\"result\": [ 0 ]}", msg->str());
EXPECT_EQ(0, result);
EXPECT_EQ(2, mccs.getValue("item1")->intValue());
addMessage(el("{ \"command\": [ \"config_update\", { \"item1\": \"asdf\" } ] }"), "Spec2", "*");
result = mccs.checkCommand();
EXPECT_EQ(1, msg_queue->size());
msg = getFirstMessage(group, to);
EXPECT_EQ("{\"result\": [ 2, \"Error in config validation: Type mismatch\" ]}", msg->str());
EXPECT_EQ(0, result);
EXPECT_EQ(2, mccs.getValue("item1")->intValue());
addMessage(el("{ \"command\": [ \"config_update\", { \"item1\": 5 } ] }"), "Spec2", "*");
result = mccs.checkCommand();
EXPECT_EQ(1, msg_queue->size());
msg = getFirstMessage(group, to);
EXPECT_EQ("{\"result\": [ 6, \"I do not like the number 5\" ]}", msg->str());
EXPECT_EQ(0, result);
EXPECT_EQ(2, mccs.getValue("item1")->intValue());
endFakeSession();
}
// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
// $Id: session.cc 1250 2010-03-09 22:52:15Z jinmei $
#include "config.h"
#include <stdint.h>
#include <cstdio>
#include <vector>
#include <iostream>
#include <sstream>
#ifdef HAVE_BOOSTLIB
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/asio.hpp>
#endif
#include <boost/foreach.hpp>
#include <exceptions/exceptions.h>
#include <cc/data.h>
#include "fake_session.h"
using namespace std;
using namespace isc::cc;
using namespace isc::data;
#ifdef HAVE_BOOSTLIB
// some of the boost::asio names conflict with socket API system calls
// (e.g. write(2)) so we don't import the entire boost::asio namespace.
using boost::asio::io_service;
using boost::asio::ip::tcp;
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
// ok i want these in cc/data
static bool
listContains(ElementPtr list, ElementPtr el)
{
BOOST_FOREACH(ElementPtr l_el, list->listValue()) {
if (l_el == el) {
return true;
}
}
return false;
}
static void
listRemove(ElementPtr list, ElementPtr el) {
int i = -1;
BOOST_FOREACH(ElementPtr s_el, list->listValue()) {
if (el == s_el) {
i = 0;
}
}
if (i >= 0) {
list->remove(i);
}
}
// endwant
ElementPtr
getFirstMessage(std::string& group, std::string& to)
{
ElementPtr el;
if (msg_queue && msg_queue->size() > 0) {
el = msg_queue->get(0);
msg_queue->remove(0);
group = el->get(0)->stringValue();
to = el->get(1)->stringValue();
return el->get(2);
} else {
group = "";
to = "";
return ElementPtr();
}
}
void
addMessage(ElementPtr msg, const std::string& group, const std::string& to)
{
ElementPtr m_el = Element::createFromString("[]");
m_el->add(Element::create(group));
m_el->add(Element::create(to));
m_el->add(msg);
if (!msg_queue) {
msg_queue = Element::createFromString("[]");
}
msg_queue->add(m_el);
}
bool
haveSubscription(const std::string& group, const std::string& instance)
{
ElementPtr s1 = Element::createFromString("[]");
ElementPtr s2 = Element::createFromString("[]");
s1->add(Element::create(group));
s1->add(Element::create(instance));
s2->add(Element::create(group));
s2->add(Element::create(instance));
return (listContains(subscriptions, s1) || listContains(subscriptions, s2));
}
bool
haveSubscription(const ElementPtr group, const ElementPtr instance)
{
return haveSubscription(group->stringValue(), instance->stringValue());
}
namespace isc {
namespace cc {
Session::Session()
{
}
#ifdef HAVE_BOOSTLIB
Session::Session(io_service& io_service UNUSED_PARAM)
{
}
#endif
Session::~Session() {
}
void
Session::disconnect() {
}
int
Session::getSocket() const {
return 1;
}
void