text:
// Copyright (c) 2010-2014 Ryan Ginstrom
// Copyright (c) 2014 Freescale Semiconductor, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#define CPPTEMPL_UNIT_TEST
#include "cpptempl.h"
#include "cpptempl.cpp"

#define BOOST_TEST_ALTERNATIVE_INIT_API
#define BOOST_TEST_MODULE cpptemplTests

#include <boost/test/unit_test.hpp>

#pragma warning(disable : 4996) // doesn't like wcstombs

#include "unit_testing.h"

#include <utility>
#include <exception>
#include <cstdint>

using namespace boost::unit_test;
using namespace std;
using namespace cpptempl;
using namespace cpptempl::impl;

std::string gettext(node_ptr node, data_map &data)
{
    ostringstream stream;
    node->gettext(stream, data);
    return stream.str();
}

// ------------------------------------------------------------------------------------------

BOOST_AUTO_TEST_SUITE(TestCppData)

// DataMap
BOOST_AUTO_TEST_CASE(test_DataMap_getvalue)
{
    data_map items;
    data_ptr data(new DataMap(items));
    BOOST_CHECK_THROW(data->getvalue(), TemplateException);
}
BOOST_AUTO_TEST_CASE(test_DataMap_getlist_throws)
{
    data_map items;
    data_ptr data(new DataMap(items));

    BOOST_CHECK_THROW(data->getlist(), TemplateException);
}
BOOST_AUTO_TEST_CASE(test_DataMap_getitem)
{
    data_map items;
    items["key"] = data_ptr(new DataValue("foo"));
    data_ptr data(new DataMap(items));

    BOOST_CHECK_EQUAL(data->getmap()["key"]->getvalue(), "foo");
}
BOOST_AUTO_TEST_CASE(test_DataMap_parse_path)
{
    data_map items;
    data_map foo;
    data_map bar;
    data_map baz;
    items["a"] = "a";
    foo["b"] = "b";
    bar["c"] = "c";
    baz["d"] = "d";
    bar["baz"] = baz;
    foo["bar"] = bar;
    items["foo"] = foo;
    BOOST_CHECK_EQUAL(items.parse_path("a")->getvalue(), "a");
    BOOST_CHECK_EQUAL(items.parse_path("foo.b")->getvalue(), "b");
    BOOST_CHECK_EQUAL(items.parse_path("foo.bar.c")->getvalue(), "c");
    BOOST_CHECK_EQUAL(items.parse_path("foo.bar.baz.d")->getvalue(), "d");
    BOOST_CHECK_THROW(items.parse_path("xx.yy"), data_map::key_error);
    BOOST_CHECK_THROW(items.parse_path("foo.bar.yy"), data_map::key_error);
}
BOOST_AUTO_TEST_CASE(test_DataMap_parent)
{
    data_map items;
    items["key"] = "foo";
    items["a"] = "zz";
    data_ptr data(new DataMap(items));

    BOOST_CHECK_EQUAL(data->getmap()["key"]->getvalue(), "foo");
    BOOST_CHECK_EQUAL(data->getmap()["a"]->getvalue(), "zz");

    data_map child;
    child["key"] = "bar";
    data_ptr data2(new DataMap(child));
    BOOST_CHECK_EQUAL(data2->getmap()["key"]->getvalue(), "bar");
    BOOST_CHECK_EQUAL(data2->getmap().has("a"), false);

    data2->getmap().set_parent(&items);
    BOOST_CHECK_EQUAL(data2->getmap()["key"]->getvalue(), "bar");
    BOOST_CHECK_EQUAL(data2->getmap()["a"]->getvalue(), "zz");
}
BOOST_AUTO_TEST_CASE(test_DataMap_parse_path_with_parent)
{
    data_map items;
    items["key"] = "foo";
    items["a"] = "zz";
    data_ptr data(new DataMap(items));

    BOOST_CHECK_EQUAL(data->getmap().parse_path("key")->getvalue(), "foo");
    BOOST_CHECK_EQUAL(data->getmap().parse_path("a")->getvalue(), "zz");

    data_map child;
    child["key"] = "bar";
    data_ptr data2(new DataMap(child));
    BOOST_CHECK_EQUAL(data2->getmap().parse_path("key")->getvalue(), "bar");
    BOOST_CHECK_EQUAL(data2->getmap().has("a"), false);

    data2->getmap().set_parent(&items);
    BOOST_CHECK_EQUAL(data2->getmap().parse_path("key")->getvalue(), "bar");
    BOOST_CHECK_EQUAL(data2->getmap().parse_path("a")->getvalue(), "zz");
}
BOOST_AUTO_TEST_CASE(test_DataMap_move)
{
    data_map items;
    items["key"] = "foo";
    data_ptr data(new DataMap(std::move(items)));

    BOOST_CHECK_EQUAL(data->getmap()["key"]->getvalue(), "foo");
    BOOST_CHECK(!items.has("key"));
}
// DataList
BOOST_AUTO_TEST_CASE(test_DataList_getvalue)
{
    data_list items;
    data_ptr data(new DataList(items));

    BOOST_CHECK_THROW(data->getvalue(), TemplateException);
}
BOOST_AUTO_TEST_CASE(test_DataList_getlist_throws)
{
    data_list items;
    items.push_back(make_data("bar"));
    data_ptr data(new DataList(items));

    BOOST_CHECK_EQUAL(data->getlist().size(), 1u);
}
BOOST_AUTO_TEST_CASE(test_DataList_getitem_throws)
{
    data_list items;
    data_ptr data(new DataList(items));

    BOOST_CHECK_THROW(data->getmap(), TemplateException);
}
BOOST_AUTO_TEST_CASE(test_DataList_move)
{
    data_list items;
    items.push_back("a");
    items.push_back("b");
    BOOST_CHECK_EQUAL(items.size(), 2);

    data_ptr data(new DataList(std::move(items)));

    BOOST_CHECK_EQUAL(data->getlist().size(), 2);
    BOOST_CHECK_EQUAL(items.size(), 0);
    BOOST_CHECK_EQUAL(data->getlist()[0]->getvalue(), "a");
}
// DataValue
BOOST_AUTO_TEST_CASE(test_DataValue_getvalue)
{
    data_ptr data(new DataValue("foo"));

    BOOST_CHECK_EQUAL(data->getvalue(), "foo");
}
BOOST_AUTO_TEST_CASE(test_DataValue_getlist_throws)
{
    data_ptr data(new DataValue("foo"));

    BOOST_CHECK_THROW(data->getlist(), TemplateException);
}
BOOST_AUTO_TEST_CASE(test_DataValue_getitem_throws)
{
    data_ptr data(new DataValue("foo"));

    BOOST_CHECK_THROW(data->getmap(), TemplateException);
}
BOOST_AUTO_TEST_CASE(test_DataValue_move)
{
    string foo = "foo";
    BOOST_CHECK_EQUAL(foo, "foo");

    data_ptr data(new DataValue(std::move(foo)));

    BOOST_CHECK_EQUAL(data->getvalue(), "foo");
    BOOST_CHECK_EQUAL(foo.size(), 0);
}
// DataBool
BOOST_AUTO_TEST_CASE(test_DataBool_true)
{
    data_ptr data(new DataBool(true));

    BOOST_CHECK_EQUAL(data->getvalue(), "true");
    BOOST_CHECK(!data->empty());
}
BOOST_AUTO_TEST_CASE(test_DataBool_false)
{
    data_ptr data(new DataBool(false));

    BOOST_CHECK_EQUAL(data->getvalue(), "false");
    BOOST_CHECK(data->empty());
}
BOOST_AUTO_TEST_CASE(test_DataBool_getitem_throws)
{
    data_ptr data(new DataBool(false));

    BOOST_CHECK_THROW(data->getmap(), TemplateException);
}
// DataInt
BOOST_AUTO_TEST_CASE(test_DataInt_0)
{
    DataInt *i = new DataInt(0);

    BOOST_CHECK_EQUAL(i->getint(), 0);
    BOOST_CHECK_EQUAL(i->getvalue(), "0");
    BOOST_CHECK(i->empty());
}
BOOST_AUTO_TEST_CASE(test_DataInt_pos)
{
    DataInt *i = new DataInt(10);

    BOOST_CHECK_EQUAL(i->getint(), 10);
    BOOST_CHECK_EQUAL(i->getvalue(), "10");
    BOOST_CHECK(!i->empty());
}
BOOST_AUTO_TEST_CASE(test_DataInt_neg)
{
    DataInt *i = new DataInt(-10);

    BOOST_CHECK_EQUAL(i->getint(), -10);
    BOOST_CHECK_EQUAL(i->getvalue(), "-10");
    BOOST_CHECK(!i->empty());
}
BOOST_AUTO_TEST_CASE(test_DataInt_unsigned)
{
    DataInt *i = new DataInt(10U);

    BOOST_CHECK_EQUAL(i->getint(), 10);
    BOOST_CHECK_EQUAL(i->getvalue(), "10");
    BOOST_CHECK(!i->empty());
}
BOOST_AUTO_TEST_CASE(test_DataInt_max_signed)
{
    DataInt *i = new DataInt(INT32_MAX);

    BOOST_CHECK_EQUAL(i->getint(), INT32_MAX);
    BOOST_CHECK_EQUAL(i->getvalue(), "2147483647");
    BOOST_CHECK(!i->empty());
}
BOOST_AUTO_TEST_CASE(test_DataInt_max_unsigned)
{
    DataInt *i = new DataInt(UINT32_MAX);

    BOOST_CHECK_EQUAL(i->getint(), -1);
    BOOST_CHECK_EQUAL(i->getvalue(), "-1");
    BOOST_CHECK(!i->empty());
}
BOOST_AUTO_TEST_CASE(test_DataInt_wrapped)
{
    data_ptr data(new DataInt(10));

    BOOST_CHECK_EQUAL(data->getint(), 10);
    BOOST_CHECK_EQUAL(data->getvalue(), "10");
    BOOST_CHECK(!data->empty());
}
BOOST_AUTO_TEST_CASE(test_DataInt_getitem_throws)
{
    data_ptr data(new DataInt(10));

    BOOST_CHECK_THROW(data->getmap(), TemplateException);
}
BOOST_AUTO_TEST_CASE(test_DataInt_not_template)
{
    data_ptr data(new DataInt(10));

    BOOST_CHECK(!data.is_template());
}
// DataTemplate
BOOST_AUTO_TEST_CASE(test_DataTemplate_empty)
{
    DataTemplate t("");
    BOOST_CHECK(!t.empty()); // templates are always non-empty
    BOOST_CHECK_EQUAL(t.getvalue(), "");
    BOOST_CHECK_EQUAL(t.params().size(), 0u);
}
BOOST_AUTO_TEST_CASE(test_DataTemplate_is_template)
{
    data_ptr d(new DataTemplate(""));
    BOOST_CHECK(d.is_template());
}
BOOST_AUTO_TEST_SUITE_END()

// ------------------------------------------------------------------------------------------

BOOST_AUTO_TEST_SUITE(TestCppStmtTokenizer)
BOOST_AUTO_TEST_CASE(test_empty)
{
    token_vector t = tokenize_statement("");
    BOOST_CHECK_EQUAL(t.size(), 0u);
}
BOOST_AUTO_TEST_CASE(test_empty_comment)
{
    token_vector t = tokenize_statement("-- hi there");
    BOOST_CHECK_EQUAL(t.size(), 0u);
}
BOOST_AUTO_TEST_CASE(test_key_path)
{
    token_vector t = tokenize_statement("foo");
    BOOST_CHECK_EQUAL(t.size(), 1u);
    BOOST_CHECK_EQUAL(t[0].get_type(), KEY_PATH_TOKEN);
    BOOST_CHECK_EQUAL(t[0].get_value(), "foo");

    t = tokenize_statement(" foo.bar ");
    BOOST_CHECK_EQUAL(t.size(), 1u);
    BOOST_CHECK_EQUAL(t[0].get_type(), KEY_PATH_TOKEN);
    BOOST_CHECK_EQUAL(t[0].get_value(), "foo.bar");

    t = tokenize_statement("   foo.monkey.helicopter  \n  spaz.umbrella");
    BOOST_CHECK_EQUAL(t.size(), 2u);
    BOOST_CHECK_EQUAL(t[0].get_type(), KEY_PATH_TOKEN);
    BOOST_CHECK_EQUAL(t[0].get_value(), "foo.monkey.helicopter");
    BOOST_CHECK_EQUAL(t[1].get_type(), KEY_PATH_TOKEN);
    BOOST_CHECK_EQUAL(t[1].get_value(), "spaz.umbrella");
}
BOOST_AUTO_TEST_CASE(test_str_lit)
{
    token_vector t = tokenize_statement("'abc'");
    BOOST_CHECK_EQUAL(t.size(), 1u);
    BOOST_CHECK_EQUAL(t[0].get_type(), STRING_LITERAL_TOKEN);
    BOOST_CHECK_EQUAL(t[0].get_value(), "abc");

    t = tokenize_statement("\"abc\"");
    BOOST_CHECK_EQUAL(t.size(), 1u);
    BOOST_CHECK_EQUAL(t[0].get_type(), STRING_LITERAL_TOKEN);
    BOOST_CHECK_EQUAL(t[0].get_value(), "abc");
}
BOOST_AUTO_TEST_CASE(test_str_lit_spaces)
{
    token_vector t = tokenize_statement("'abc def'");
    BOOST_CHECK_EQUAL(t.size(), 1u);
    BOOST_CHECK_EQUAL(t[0].get_type(), STRING_LITERAL_TOKEN);
    BOOST_CHECK_EQUAL(t[0].get_value(), "abc def");

    t = tokenize_statement("\"abc def\"");
    BOOST_CHECK_EQUAL(t.size(), 1u);
    BOOST_CHECK_EQUAL(t[0].get_type(), STRING_LITERAL_TOKEN);
    BOOST_CHECK_EQUAL(t[0].get_value(), "abc def");
}
BOOST_AUTO_TEST_CASE(test_str_lit_escapes)
{
    token_vector t = tokenize_statement("'\\x41'");
    BOOST_CHECK_EQUAL(t.size(), 1u);
    BOOST_CHECK_EQUAL(t[0].get_type(), STRING_LITERAL_TOKEN);
    BOOST_CHECK_EQUAL(t[0].get_value(), "A");

    t = tokenize_statement("'he said,\\n\\\"hello\\\"'");
    BOOST_CHECK_EQUAL(t.size(), 1u);
    BOOST_CHECK_EQUAL(t[0].get_type(), STRING_LITERAL_TOKEN);
    BOOST_CHECK_EQUAL(t[0].get_value(), "he said,\n\"hello\"");
}
BOOST_AUTO_TEST_CASE(test_bool_lit)
{
    token_vector t = tokenize_statement("true false");
    BOOST_CHECK_EQUAL(t.size(), 2u);
    BOOST_CHECK_EQUAL(t[0].get_type(), TRUE_TOKEN);
    BOOST_CHECK_EQUAL(t[1].get_type(), FALSE_TOKEN);
}
BOOST_AUTO_TEST_CASE(test_int_lit)
{
    token_vector t = tokenize_statement("123");
    BOOST_CHECK_EQUAL(t.size(), 1u);
    BOOST_CHECK_EQUAL(t[0].get_type(), INT_LITERAL_TOKEN);
    BOOST_CHECK_EQUAL(t[0].get_value(), "123");
}
BOOST_AUTO_TEST_CASE(test_int_lit_hex)
{
    token_vector t = tokenize_statement("0x123");
    BOOST_CHECK_EQUAL(t.size(), 1u);
    BOOST_CHECK_EQUAL(t[0].get_type(), INT_LITERAL_TOKEN);
    BOOST_CHECK_EQUAL(t[0].get_value(), "0x123");
}
BOOST_AUTO_TEST_CASE(test_op)
{
    token_vector t = tokenize_statement("== != && || and or ! not ( ) ,");
    BOOST_CHECK_EQUAL(t.size(), 11u);
    BOOST_CHECK_EQUAL(t[0].get_type(), EQ_TOKEN);
    BOOST_CHECK_EQUAL(t[1].get_type(), NEQ_TOKEN);
    BOOST_CHECK_EQUAL(t[2].get_type(), AND_TOKEN);
    BOOST_CHECK_EQUAL(t[3].get_type(), OR_TOKEN);
    BOOST_CHECK_EQUAL(t[4].get_type(), AND_TOKEN);
    BOOST_CHECK_EQUAL(t[5].get_type(), OR_TOKEN);
    BOOST_CHECK_EQUAL(t[6].get_type(), NOT_TOKEN);
    BOOST_CHECK_EQUAL(t[7].get_type(), NOT_TOKEN);
    BOOST_CHECK_EQUAL(t[8].get_type(), OPEN_PAREN_TOKEN);
    BOOST_CHECK_EQUAL(t[9].get_type(), CLOSE_PAREN_TOKEN);
    BOOST_CHECK_EQUAL(t[10].get_type(), COMMA_TOKEN);

    t = tokenize_statement("==!=&&||and or!not(),");
    BOOST_CHECK_EQUAL(t.size(), 11u);
    BOOST_CHECK_EQUAL(t[0].get_type(), EQ_TOKEN);
    BOOST_CHECK_EQUAL(t[1].get_type(), NEQ_TOKEN);
    BOOST_CHECK_EQUAL(t[2].get_type(), AND_TOKEN);
    BOOST_CHECK_EQUAL(t[3].get_type(), OR_TOKEN);
    BOOST_CHECK_EQUAL(t[4].get_type(), AND_TOKEN);
    BOOST_CHECK_EQUAL(t[5].get_type(), OR_TOKEN);
    BOOST_CHECK_EQUAL(t[6].get_type(), NOT_TOKEN);
    BOOST_CHECK_EQUAL(t[7].get_type(), NOT_TOKEN);
    BOOST_CHECK_EQUAL(t[8].get_type(), OPEN_PAREN_TOKEN);
    BOOST_CHECK_EQUAL(t[9].get_type(), CLOSE_PAREN_TOKEN);
    BOOST_CHECK_EQUAL(t[10].get_type(), COMMA_TOKEN);
}
BOOST_AUTO_TEST_CASE(test_op2)
{
    token_vector t = tokenize_statement("+ - * / % &");
    BOOST_CHECK_EQUAL(t.size(), 6u);
    BOOST_CHECK_EQUAL(t[0].get_type(), PLUS_TOKEN);
    BOOST_CHECK_EQUAL(t[1].get_type(), MINUS_TOKEN);
    BOOST_CHECK_EQUAL(t[2].get_type(), TIMES_TOKEN);
    BOOST_CHECK_EQUAL(t[3].get_type(), DIVIDE_TOKEN);
    BOOST_CHECK_EQUAL(t[4].get_type(), MOD_TOKEN);
    BOOST_CHECK_EQUAL(t[5].get_type(), CONCAT_TOKEN);

    t = tokenize_statement("+-*/%&");
    BOOST_CHECK_EQUAL(t.size(), 6u);
    BOOST_CHECK_EQUAL(t[0].get_type(), PLUS_TOKEN);
    BOOST_CHECK_EQUAL(t[1].get_type(), MINUS_TOKEN);
    BOOST_CHECK_EQUAL(t[2].get_type(), TIMES_TOKEN);
    BOOST_CHECK_EQUAL(t[3].get_type(), DIVIDE_TOKEN);
    BOOST_CHECK_EQUAL(t[4].get_type(), MOD_TOKEN);
    BOOST_CHECK_EQUAL(t[5].get_type(), CONCAT_TOKEN);
}
BOOST_AUTO_TEST_CASE(test_op3)
{
    token_vector t = tokenize_statement("> >= < <=");
    BOOST_CHECK_EQUAL(t.size(), 4u);
    BOOST_CHECK_EQUAL(t[0].get_type(), GT_TOKEN);
    BOOST_CHECK_EQUAL(t[1].get_type(), GE_TOKEN);
    BOOST_CHECK_EQUAL(t[2].get_type(), LT_TOKEN);
    BOOST_CHECK_EQUAL(t[3].get_type(), LE_TOKEN);

    t = tokenize_statement(">>=<<=");
    BOOST_CHECK_EQUAL(t.size(), 4u);
    BOOST_CHECK_EQUAL(t[0].get_type(), GT_TOKEN);
    BOOST_CHECK_EQUAL(t[1].get_type(), GE_TOKEN);
    BOOST_CHECK_EQUAL(t[2].get_type(), LT_TOKEN);
    BOOST_CHECK_EQUAL(t[3].get_type(), LE_TOKEN);
}
BOOST_AUTO_TEST_CASE(test_kwd)
{
    token_vector t = tokenize_statement("for in if elif else def endfor endif enddef");
    BOOST_CHECK_EQUAL(t.size(), 9u);
    BOOST_CHECK_EQUAL(t[0].get_type(), FOR_TOKEN);
    BOOST_CHECK_EQUAL(t[1].get_type(), IN_TOKEN);
    BOOST_CHECK_EQUAL(t[2].get_type(), IF_TOKEN);
    BOOST_CHECK_EQUAL(t[3].get_type(), ELIF_TOKEN);
    BOOST_CHECK_EQUAL(t[4].get_type(), ELSE_TOKEN);
    BOOST_CHECK_EQUAL(t[5].get_type(), DEF_TOKEN);
    BOOST_CHECK_EQUAL(t[6].get_type(), ENDFOR_TOKEN);
    BOOST_CHECK_EQUAL(t[7].get_type(), ENDIF_TOKEN);
    BOOST_CHECK_EQUAL(t[8].get_type(), ENDDEF_TOKEN);
}
BOOST_AUTO_TEST_CASE(test_token_iterator_empty)
{
    token_vector t = tokenize_statement("");
    TokenIterator i(t);
    BOOST_CHECK_EQUAL(i.size(), 0u);
    BOOST_CHECK(i.empty());
    BOOST_CHECK(!i.is_valid());
    BOOST_CHECK_EQUAL(i.get()->get_type(), END_TOKEN);
    ++i;
    BOOST_CHECK_EQUAL(i.get()->get_type(), END_TOKEN);
    BOOST_CHECK_THROW(i.match(EQ_TOKEN), TemplateException);
}
BOOST_AUTO_TEST_CASE(test_token_iterator_notempty)
{
    token_vector t = tokenize_statement("key.path == 'str'");
    TokenIterator i(t);
    BOOST_CHECK_EQUAL(i.size(), 3u);
    BOOST_CHECK(!i.empty());
    BOOST_CHECK(i.is_valid());
    BOOST_CHECK_EQUAL(i.get()->get_type(), KEY_PATH_TOKEN);
    ++i;
    BOOST_CHECK(i.is_valid());
    BOOST_CHECK_EQUAL(i.get()->get_type(), EQ_TOKEN);
    BOOST_CHECK_EQUAL(i.match(EQ_TOKEN)->get_type(), EQ_TOKEN);
    BOOST_CHECK(i.is_valid());
    BOOST_CHECK_EQUAL(i.get()->get_type(), STRING_LITERAL_TOKEN);
    BOOST_CHECK_EQUAL(i.match(STRING_LITERAL_TOKEN)->get_type(), STRING_LITERAL_TOKEN);
}
BOOST_AUTO_TEST_SUITE_END()

// ------------------------------------------------------------------------------------------

BOOST_AUTO_TEST_SUITE(TestCppNode)

// NodeVar
BOOST_AUTO_TEST_CASE(TestNodeVarType)
{
    token_vector tokens = impl::tokenize_statement("foo");
    NodeVar node(tokens);
    BOOST_CHECK_EQUAL(node.gettype(), NODE_TYPE_VAR);
}
BOOST_AUTO_TEST_CASE(TestNodeVar)
{
    node_ptr node(new NodeVar(tokenize_statement("foo")));
    data_map data;
    data["foo"] = make_data("bar");
    BOOST_CHECK_EQUAL(gettext(node, data), "bar");
}
BOOST_AUTO_TEST_CASE(TestNodeVarCantHaveChildren)
{
    NodeVar node(tokenize_statement("foo"));
    node_vector children;
    BOOST_CHECK_THROW(node.set_children(children), TemplateException);
}
// NodeText
BOOST_AUTO_TEST_CASE(TestNodeTextType)
{
    NodeText node("foo");
    BOOST_CHECK_EQUAL(node.gettype(), NODE_TYPE_TEXT);
}
BOOST_AUTO_TEST_CASE(TestNodeText)
{
    node_ptr node(new NodeText("foo"));
    data_map data;
    data["foo"] = make_data("bar");
    BOOST_CHECK_EQUAL(gettext(node, data), "foo");
}
BOOST_AUTO_TEST_CASE(TestNodeTextCantHaveChildrenSet)
{
    NodeText node("foo");
    node_vector children;
    BOOST_CHECK_THROW(node.set_children(children), TemplateException);
}
BOOST_AUTO_TEST_CASE(TestNodeTextCantHaveChildrenGet)
{
    NodeText node("foo");
    node_vector children;
    BOOST_CHECK_THROW(node.get_children(), TemplateException);
}
// NodeFor
BOOST_AUTO_TEST_CASE(TestNodeForBadSyntax)
{
    BOOST_CHECK_THROW(NodeFor node(tokenize_statement("foo"), true), TemplateException);
}
BOOST_AUTO_TEST_CASE(TestNodeForType)
{
    NodeFor node(tokenize_statement("for item in items"), true);
    BOOST_CHECK_EQUAL(node.gettype(), NODE_TYPE_FOR);
}
BOOST_AUTO_TEST_CASE(TestNodeForTextEmpty)
{
    node_ptr node(new NodeFor(tokenize_statement("for item in items"), true));
    data_map data;
    data_list items;
    items.push_back(make_data("first"));
    data["items"] = make_data(items);
    BOOST_CHECK_EQUAL(gettext(node, data), "");
}
BOOST_AUTO_TEST_CASE(TestNodeForTextOneVar)
{
    node_vector children;
    children.push_back(node_ptr(new NodeVar(tokenize_statement("item"))));
    node_ptr node(new NodeFor(tokenize_statement("for item in items"), true));
    node->set_children(children);
    data_map data;
    data_list items;
    items.push_back(make_data("first "));
    items.push_back(make_data("second "));
    data["items"] = make_data(items);
    BOOST_CHECK_EQUAL(gettext(node, data), "first second ");
}
BOOST_AUTO_TEST_CASE(TestNodeForTextOneVarLoop)
{
    node_vector children;
    children.push_back(node_ptr(new NodeVar(tokenize_statement("loop.index"))));
    node_ptr node(new NodeFor(tokenize_statement("for item in items"), true));
    node->set_children(children);
    data_map data;
    data_list items;
    items.push_back(make_data("first "));
    items.push_back(make_data("second "));
    data["items"] = make_data(items);
    BOOST_CHECK_EQUAL(gettext(node, data), "12");
}
BOOST_AUTO_TEST_CASE(TestNodeForLoopTextVar)
{
    node_vector children;
    children.push_back(node_ptr(new NodeVar(tokenize_statement("loop.index"))));
    children.push_back(node_ptr(new NodeText(". ")));
    children.push_back(node_ptr(new NodeVar(tokenize_statement("item"))));
    children.push_back(node_ptr(new NodeText(" ")));
    node_ptr node(new NodeFor(tokenize_statement("for item in items"), true));
    node->set_children(children);
    data_map data;
    data_list items;
    items.push_back(make_data("first"));
    items.push_back(make_data("second"));
    data["items"] = make_data(items);
    BOOST_CHECK_EQUAL(gettext(node, data), "1. first 2. second ");
}
BOOST_AUTO_TEST_CASE(TestNodeForLoopTextVarDotted)
{
    node_vector children;
    children.push_back(node_ptr(new NodeVar(tokenize_statement("loop.index"))));
    children.push_back(node_ptr(new NodeText(". ")));
    children.push_back(node_ptr(new NodeVar(tokenize_statement("friend.name"))));
    children.push_back(node_ptr(new NodeText(" ")));
    node_ptr node(new NodeFor(tokenize_statement("for friend in person.friends"), true));
    node->set_children(children);

    data_map bob;
    bob["name"] = make_data("Bob");
    data_map betty;
    betty["name"] = make_data("Betty");
    data_list friends;
    friends.push_back(make_data(bob));
    friends.push_back(make_data(betty));
    data_map person;
    person["friends"] = make_data(friends);
    data_map data;
    data["person"] = make_data(person);

    BOOST_CHECK_EQUAL(gettext(node, data), "1. Bob 2. Betty ");
}
BOOST_AUTO_TEST_CASE(TestNodeForTextOneText)
{
    node_vector children;
    children.push_back(node_ptr(new NodeText("{--}")));
    node_ptr node(new NodeFor(tokenize_statement("for item in items"), true));
    node->set_children(children);
    data_map data;
    data_list items;
    items.push_back(make_data("first "));
    items.push_back(make_data("second "));
    data["items"] = make_data(items);
    BOOST_CHECK_EQUAL(gettext(node, data), "{--}{--}");
}

//////////////////////////////////////////////////////////////////////////
// NodeIf
//////////////////////////////////////////////////////////////////////////

BOOST_AUTO_TEST_CASE(TestNodeIfType)
{
    NodeIf node(tokenize_statement("if items"));
    BOOST_CHECK_EQUAL(node.gettype(), NODE_TYPE_IF);
}
// if not empty
BOOST_AUTO_TEST_CASE(TestNodeIfText)
{
    node_vector children;
    children.push_back(node_ptr(new NodeText("{--}")));
    node_ptr node(new NodeIf(tokenize_statement("if item")));
    node->set_children(children);
    data_map data;
    data["item"] = make_data("foo");
    BOOST_CHECK_EQUAL(gettext(node, data), "{--}");
    data["item"] = false;
    BOOST_CHECK_EQUAL(gettext(node, data), "");
}
BOOST_AUTO_TEST_CASE(TestNodeIfVar)
{
    node_vector children;
    children.push_back(node_ptr(new NodeVar(tokenize_statement("item"))));
    node_ptr node(new NodeIf(tokenize_statement("if item")));
    node->set_children(children);
    data_map data;
    data["item"] = make_data("foo");
    BOOST_CHECK_EQUAL(gettext(node, data), "foo");
    data["item"] = false;
    BOOST_CHECK_EQUAL(gettext(node, data), "");
}

// ==
BOOST_AUTO_TEST_CASE(TestNodeIfEqualsTrue)
{
    node_vector children;
    children.push_back(node_ptr(new NodeVar(tokenize_statement("item"))));
    node_ptr node(new NodeIf(tokenize_statement("if item == \"foo\"")));
    node->set_children(children);
    data_map data;
    data["item"] = make_data("foo");
    BOOST_CHECK_EQUAL(gettext(node, data), "foo");
}
BOOST_AUTO_TEST_CASE(TestNodeIfEqualsFalse)
{
    node_vector children;
    children.push_back(node_ptr(new NodeVar(tokenize_statement("item"))));
    node_ptr node(new NodeIf(tokenize_statement("if item == \"bar\"")));
    node->set_children(children);
    data_map data;
    data["item"] = make_data("foo");
    BOOST_CHECK_EQUAL(gettext(node, data), "");
}
BOOST_AUTO_TEST_CASE(TestNodeIfEqualsTwoVarsTrue)
{
    node_vector children;
    children.push_back(node_ptr(new NodeVar(tokenize_statement("item"))));
    node_ptr node(new NodeIf(tokenize_statement("if item == foo")));
    node->set_children(children);
    data_map data;
    data["item"] = make_data("x");
    data["foo"] = make_data("x");
    BOOST_CHECK_EQUAL(gettext(node, data), "x");
    data["foo"] = make_data("z");
    BOOST_CHECK_EQUAL(gettext(node, data), "");
}

// !=
BOOST_AUTO_TEST_CASE(TestNodeIfNotEqualsTrue)
{
    node_vector children;
    children.push_back(node_ptr(new NodeVar(tokenize_statement("item"))));
    node_ptr node(new NodeIf(tokenize_statement("if item != \"foo\"")));
    node->set_children(children);
    data_map data;
    data["item"] = make_data("foo");
    BOOST_CHECK_EQUAL(gettext(node, data), "");
}
BOOST_AUTO_TEST_CASE(TestNodeIfNotEqualsFalse)
{
    node_vector children;
    children.push_back(node_ptr(new NodeVar(tokenize_statement("item"))));
    node_ptr node(new NodeIf(tokenize_statement("if item != \"bar\"")));
    node->set_children(children);
    data_map data;
    data["item"] = make_data("foo");
    BOOST_CHECK_EQUAL(gettext(node, data), "foo");
}

// not
BOOST_AUTO_TEST_CASE(TestNodeIfNotTrueText)
{
    node_vector children;
    children.push_back(node_ptr(new NodeText("{--}")));
    node_ptr node(new NodeIf(tokenize_statement("if not item")));
    node->set_children(children);
    data_map data;
    data["item"] = make_data("foo");
    BOOST_CHECK_EQUAL(gettext(node, data), "");
}
BOOST_AUTO_TEST_CASE(TestNodeIfNotFalseText)
{
    node_vector children;
    children.push_back(node_ptr(new NodeText("{--}")));
    node_ptr node(new NodeIf(tokenize_statement("if not item")));
    node->set_children(children);
    data_map data;
    data["item"] = make_data("");
    BOOST_CHECK_EQUAL(gettext(node, data), "{--}");
}

BOOST_AUTO_TEST_SUITE_END()

// ------------------------------------------------------------------------------------------

BOOST_AUTO_TEST_SUITE(TestCppExprParser)

BOOST_AUTO_TEST_CASE(test_get_var_value_simple)
{
    token_vector v;
    TokenIterator t(v);
    data_map d;
    d["item"] = "a";
    ExprParser p(t, d);
    data_list params;
    data_ptr r = p.get_var_value("item", params);
    BOOST_CHECK_EQUAL(r->getvalue(), "a");
}
BOOST_AUTO_TEST_CASE(test_get_var_value_dotted)
{
    token_vector v;
    TokenIterator t(v);
    data_map d;
    data_map x;
    x["a"] = "a";
    d["x"] = x;
    ExprParser p(t, d);
    data_list params;
    data_ptr r = p.get_var_value("x.a", params);
    BOOST_CHECK_EQUAL(r->getvalue(), "a");
}
BOOST_AUTO_TEST_CASE(test_get_var_value_fn_count)
{
    token_vector v;
    TokenIterator t(v);
    data_map d;
    data_list a;
    a.push_back("1");
    a.push_back("2");
    a.push_back("3");
    ExprParser p(t, d);
    data_list params;
    params.push_back(a);
    data_ptr r = p.get_var_value("count", params);
    BOOST_CHECK_EQUAL(r->getvalue(), "3");
}
BOOST_AUTO_TEST_CASE(test_get_var_value_fn_empty)
{
    token_vector v;
    TokenIterator t(v);
    data_map d;
    ExprParser p(t, d);
    data_list params;
    params.push_back("");
    data_ptr r = p.get_var_value("empty", params);
    BOOST_CHECK_EQUAL(r->getvalue(), "true");
    params[0] = "x";
    data_ptr m = p.get_var_value("empty", params);
    BOOST_CHECK_EQUAL(m->getvalue(), "false");
}
BOOST_AUTO_TEST_CASE(test_get_var_value_fn_defined)
{
    // Placeholder until defined() is fixed.
}
BOOST_AUTO_TEST_CASE(test_str_lit)
{
    token_vector v = tokenize_statement("'hi'");
    TokenIterator t(v);
    data_map d;
    ExprParser p(t, d);
    data_ptr r = p.parse_expr();
    BOOST_CHECK_EQUAL(r->getvalue(), "hi");
}
BOOST_AUTO_TEST_CASE(test_int_lit)
{
    token_vector v = tokenize_statement("123");
    TokenIterator t(v);
    data_map d;
    ExprParser p(t, d);
    data_ptr r = p.parse_expr();
    BOOST_CHECK_EQUAL(r->getint(), 123);
}
BOOST_AUTO_TEST_CASE(test_int_lit_hex)
{
    token_vector v = tokenize_statement("0x1000");
    TokenIterator t(v);
    data_map d;
    ExprParser p(t, d);
    data_ptr r = p.parse_expr();
    BOOST_CHECK_EQUAL(r->getint(), 4096);
}
BOOST_AUTO_TEST_CASE(test_and)
{
    token_vector v = tokenize_statement("a and b");
    TokenIterator t(v);
    data_map d;
    d["a"] = false;
    d["b"] = false;
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
    d["a"] = true;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
    d["a"] = false;
    d["b"] = true;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
    d["a"] = true;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
}
BOOST_AUTO_TEST_CASE(test_and_multiline_commented)
{
    token_vector v = tokenize_statement("a -- this is a\nand -- could be &&\nb -- the second operand");
    TokenIterator t(v);
    data_map d;
    d["a"] = false;
    d["b"] = false;
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
    d["a"] = true;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
    d["a"] = false;
    d["b"] = true;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
    d["a"] = true;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
}
BOOST_AUTO_TEST_CASE(test_or)
{
    token_vector v = tokenize_statement("a or b");
    TokenIterator t(v);
    data_map d;
    d["a"] = false;
    d["b"] = false;
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
    d["a"] = true;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
    d["a"] = false;
    d["b"] = true;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
    d["a"] = true;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
}
BOOST_AUTO_TEST_CASE(test_or_value)
{
    token_vector v = tokenize_statement("a or b");
    TokenIterator t(v);
    data_map d;
    d["a"] = "x";
    d["b"] = "";
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "x");
    d["a"] = "";
    d["b"] = "y";
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "y");
}
BOOST_AUTO_TEST_CASE(test_equal)
{
    token_vector v = tokenize_statement("a == b");
    TokenIterator t(v);
    data_map d;
    d["a"] = "x";
    d["b"] = "y";
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
    d["a"] = "y";
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
}
BOOST_AUTO_TEST_CASE(test_not_equal)
{
    token_vector v = tokenize_statement("a != b");
    TokenIterator t(v);
    data_map d;
    d["a"] = "x";
    d["b"] = "y";
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
    d["a"] = "y";
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
}
BOOST_AUTO_TEST_CASE(test_gt)
{
    token_vector v = tokenize_statement("a > b");
    TokenIterator t(v);
    data_map d;
    d["a"] = 200;
    d["b"] = 100;
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
    d["a"] = 50;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
    d["b"] = 50;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
}
BOOST_AUTO_TEST_CASE(test_gt_txt)
{
    token_vector v = tokenize_statement("a > b");
    TokenIterator t(v);
    data_map d;
    d["a"] = "apple";
    d["b"] = "bear";
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
    d["a"] = "monkey";
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
    d["b"] = "monkey";
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
}
BOOST_AUTO_TEST_CASE(test_ge)
{
    token_vector v = tokenize_statement("a >= b");
    TokenIterator t(v);
    data_map d;
    d["a"] = 200;
    d["b"] = 100;
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
    d["a"] = 50;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
    d["b"] = 50;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
}
BOOST_AUTO_TEST_CASE(test_ge_txt)
{
    token_vector v = tokenize_statement("a >= b");
    TokenIterator t(v);
    data_map d;
    d["a"] = "apple";
    d["b"] = "bear";
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
    d["a"] = "monkey";
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
    d["b"] = "monkey";
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
}
BOOST_AUTO_TEST_CASE(test_lt)
{
    token_vector v = tokenize_statement("a < b");
    TokenIterator t(v);
    data_map d;
    d["a"] = 200;
    d["b"] = 100;
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
    d["a"] = 50;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
    d["b"] = 50;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
}
BOOST_AUTO_TEST_CASE(test_lt_txt)
{
    token_vector v = tokenize_statement("a < b");
    TokenIterator t(v);
    data_map d;
    d["a"] = "apple";
    d["b"] = "bear";
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
    d["a"] = "monkey";
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
    d["b"] = "monkey";
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
}
BOOST_AUTO_TEST_CASE(test_le)
{
    token_vector v = tokenize_statement("a <= b");
    TokenIterator t(v);
    data_map d;
    d["a"] = 200;
    d["b"] = 100;
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
    d["a"] = 50;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
    d["b"] = 50;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
}
BOOST_AUTO_TEST_CASE(test_le_txt)
{
    token_vector v = tokenize_statement("a <= b");
    TokenIterator t(v);
    data_map d;
    d["a"] = "apple";
    d["b"] = "bear";
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
    d["a"] = "monkey";
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
    d["b"] = "monkey";
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
}
BOOST_AUTO_TEST_CASE(test_str_cat)
{
    token_vector v = tokenize_statement("a & b");
    TokenIterator t(v);
    data_map d;
    d["a"] = "hello";
    d["b"] = "world";
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "helloworld");
    d["a"] = 50;
    d["b"] = true;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "50true");
}
BOOST_AUTO_TEST_CASE(test_str_cat_lit)
{
    token_vector v = tokenize_statement("\"a\" & \"b\"");
    TokenIterator t(v);
    data_map d;
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "ab");
}
BOOST_AUTO_TEST_CASE(test_add)
{
    token_vector v = tokenize_statement("a + b");
    TokenIterator t(v);
    data_map d;
    d["a"] = 200;
    d["b"] = 100;
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getint(), 300);
    d["a"] = -50;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getint(), 50);
}
BOOST_AUTO_TEST_CASE(test_sub)
{
    token_vector v = tokenize_statement("a - b");
    TokenIterator t(v);
    data_map d;
    d["a"] = 200;
    d["b"] = 100;
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getint(), 100);
    d["a"] = -50;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getint(), -150);
}
BOOST_AUTO_TEST_CASE(test_mul)
{
    token_vector v = tokenize_statement("a * b");
    TokenIterator t(v);
    data_map d;
    d["a"] = 200;
    d["b"] = 100;
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getint(), 20000);
    d["a"] = -50;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getint(), -5000);
}
BOOST_AUTO_TEST_CASE(test_div)
{
    token_vector v = tokenize_statement("a / b");
    TokenIterator t(v);
    data_map d;
    d["a"] = 200;
    d["b"] = 100;
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getint(), 2);
    d["b"] = -5;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getint(), -40);
}
BOOST_AUTO_TEST_CASE(test_mod)
{
    token_vector v = tokenize_statement("a % b");
    TokenIterator t(v);
    data_map d;
    d["a"] = 12;
    d["b"] = 10;
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getint(), 2);
    d["a"] = 10;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getint(), 0);
}
BOOST_AUTO_TEST_CASE(test_not)
{
    token_vector v = tokenize_statement("not a");
    TokenIterator t(v);
    data_map d;
    d["a"] = true;
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
    d["a"] = false;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
}
BOOST_AUTO_TEST_CASE(test_unary_minus)
{
    token_vector v = tokenize_statement("-12");
    TokenIterator t(v);
    data_map d;
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getint(), -12);
}
BOOST_AUTO_TEST_CASE(test_unary_minus_var)
{
    token_vector v = tokenize_statement("-a");
    TokenIterator t(v);
    data_map d;
    d["a"] = 100;
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getint(), -100);
}
BOOST_AUTO_TEST_CASE(test_parens)
{
    token_vector v = tokenize_statement("(a)");
    TokenIterator t(v);
    data_map d;
    d["a"] = "xyzzy";
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "xyzzy");
}
BOOST_AUTO_TEST_CASE(test_parens_or)
{
    token_vector v = tokenize_statement("(a || b) == z");
    TokenIterator t(v);
    data_map d;
    d["a"] = "";
    d["b"] = "foo";
    d["z"] = "xyzzy";
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
    d["z"] = "foo";
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
    d["a"] = "bar";
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
    d["z"] = "bar";
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
}
BOOST_AUTO_TEST_CASE(test_and_or)
{
    token_vector v = tokenize_statement("a && b || c");
    TokenIterator t(v);
    data_map d;
    d["a"] = true;
    d["b"] = true;
    d["c"] = false;
    ExprParser p(t, d);
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
    d["a"] = false;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "false");
    d["c"] = true;
    t.reset();
    BOOST_CHECK_EQUAL(p.parse_expr()->getvalue(), "true");
}

BOOST_AUTO_TEST_SUITE_END()

// ------------------------------------------------------------------------------------------

BOOST_AUTO_TEST_SUITE(TestCppParser)

BOOST_AUTO_TEST_CASE(test_empty)
{
    string text = "";
    node_vector nodes;
    impl::TemplateParser(text, nodes).parse();

    BOOST_CHECK_EQUAL(0u, nodes.size());
}
BOOST_AUTO_TEST_CASE(test_text_only)
{
    string text = "blah blah blah";
    node_vector nodes;
    impl::TemplateParser(text, nodes).parse();
    data_map data;

    BOOST_CHECK_EQUAL(1u, nodes.size());
    BOOST_CHECK_EQUAL(gettext(nodes[0], data), "blah blah blah");
}
BOOST_AUTO_TEST_CASE(test_brackets_no_var)
{
    string text = "{foo}";
    node_vector nodes;
    impl::TemplateParser(text, nodes).parse();
    data_map data;

    BOOST_CHECK_EQUAL(2u, nodes.size());
    BOOST_CHECK_EQUAL(gettext(nodes[0], data), "{");
    BOOST_CHECK_EQUAL(gettext(nodes[1], data), "foo}");
}
BOOST_AUTO_TEST_CASE(test_ends_with_bracket)
{
    string text = "blah blah blah{";
    node_vector nodes;
    impl::TemplateParser(text, nodes).parse();
    data_map data;

    BOOST_CHECK_EQUAL(2u, nodes.size());
    BOOST_CHECK_EQUAL(gettext(nodes[0], data), "blah blah blah");
    BOOST_CHECK_EQUAL(gettext(nodes[1], data), "{");
}
// var
BOOST_AUTO_TEST_CASE(test_var)
{
    string text = "{$foo}";
    node_vector nodes;
    impl::TemplateParser(text, nodes).parse();
    data_map data;
    data["foo"] = make_data("bar");

    BOOST_CHECK_EQUAL(1u, nodes.size());
    BOOST_CHECK_EQUAL(gettext(nodes[0], data), "bar");
}
// for
BOOST_AUTO_TEST_CASE(test_for)
{
    string text = "{% for item in items %}";
    node_vector nodes;
    impl::TemplateParser(text, nodes).parse();

    BOOST_CHECK_EQUAL(1u, nodes.size());
    BOOST_CHECK_EQUAL(nodes[0]->gettype(), NODE_TYPE_FOR);
}
BOOST_AUTO_TEST_CASE(test_for_full)
{
    string text = "{% for item in items %}{$item}{% endfor %}";
    node_vector nodes;
    impl::TemplateParser(text, nodes).parse();

    BOOST_CHECK_EQUAL(1u, nodes.size());
    BOOST_CHECK_EQUAL(1u, nodes[0]->get_children().size());
    BOOST_CHECK_EQUAL(nodes[0]->gettype(), NODE_TYPE_FOR);
    BOOST_CHECK_EQUAL(nodes[0]->get_children()[0]->gettype(), NODE_TYPE_VAR);
}
BOOST_AUTO_TEST_CASE(test_for_full_with_text)
{
    string text = "{% for item in items %}*{$item}*{% endfor %}";
    node_vector nodes;
    impl::TemplateParser(text, nodes).parse();
    data_map data;
    data["item"] = make_data("my ax");

    BOOST_CHECK_EQUAL(1u, nodes.size());
    BOOST_CHECK_EQUAL(3u, nodes[0]->get_children().size());
    BOOST_CHECK_EQUAL(nodes[0]->gettype(), NODE_TYPE_FOR);
    BOOST_CHECK_EQUAL(gettext(nodes[0]->get_children()[0], data), "*");
    BOOST_CHECK_EQUAL(nodes[0]->get_children()[1]->gettype(), NODE_TYPE_VAR);
    BOOST_CHECK_EQUAL(gettext(nodes[0]->get_children()[1], data), "my ax");
    BOOST_CHECK_EQUAL(gettext(nodes[0]->get_children()[2], data), "*");
}
// if
BOOST_AUTO_TEST_CASE(test_if)
{
    string text = "{% if foo %}";
    node_vector nodes;
    impl::TemplateParser(text, nodes).parse();

    BOOST_CHECK_EQUAL(1u, nodes.size());
    BOOST_CHECK_EQUAL(nodes[0]->gettype(), NODE_TYPE_IF);
}
BOOST_AUTO_TEST_CASE(test_if_full)
{
    string text = "{% if item %}{$item}{% endif %}";
    node_vector nodes;
    impl::TemplateParser(text, nodes).parse();

    BOOST_CHECK_EQUAL(1u, nodes.size());
    BOOST_CHECK_EQUAL(1u, nodes[0]->get_children().size());
    BOOST_CHECK_EQUAL(nodes[0]->gettype(), NODE_TYPE_IF);
    BOOST_CHECK_EQUAL(nodes[0]->get_children()[0]->gettype(), NODE_TYPE_VAR);
}
BOOST_AUTO_TEST_CASE(test_if_full_with_text)
{
    string text = "{% if item %}{{$item}}{% endif %}";
    node_vector nodes;
    impl::TemplateParser(text, nodes).parse();
    data_map data;
    data["item"] = make_data("my ax");

    BOOST_CHECK_EQUAL(1u, nodes.size());
    BOOST_CHECK_EQUAL(3u, nodes[0]->get_children().size());
    BOOST_CHECK_EQUAL(nodes[0]->gettype(), NODE_TYPE_IF);
    BOOST_CHECK_EQUAL(gettext(nodes[0]->get_children()[0], data), "{");
    BOOST_CHECK_EQUAL(nodes[0]->get_children()[1]->gettype(), NODE_TYPE_VAR);
    BOOST_CHECK_EQUAL(gettext(nodes[0]->get_children()[1], data), "my ax");
    BOOST_CHECK_EQUAL(gettext(nodes[0]->get_children()[2], data), "}");
}

BOOST_AUTO_TEST_SUITE_END()

// ------------------------------------------------------------------------------------------

BOOST_AUTO_TEST_SUITE(TestCppTemplateEval)

BOOST_AUTO_TEST_CASE(test_empty)
{
    string text = "";
    data_map data;
    string actual = parse(text, data);
    string expected = "";
    BOOST_CHECK_EQUAL(expected, actual);
}
BOOST_AUTO_TEST_CASE(test_no_vars)
{
    string text = "foo";
    data_map data;
    string actual = parse(text, data);
    string expected = "foo";
    BOOST_CHECK_EQUAL(expected, actual);
}
BOOST_AUTO_TEST_CASE(test_var)
{
    string text = "{$foo}";
    data_map data;
    data["foo"] = make_data("bar");
    string actual = parse(text, data);
    string expected = "bar";
    BOOST_CHECK_EQUAL(expected, actual);
}
BOOST_AUTO_TEST_CASE(test_var_surrounded)
{
    string text = "aaa{$foo}bbb";
    data_map data;
    data["foo"] = make_data("---");
    string actual = parse(text, data);
    string expected = "aaa---bbb";
    BOOST_CHECK_EQUAL(expected, actual);
}
BOOST_AUTO_TEST_CASE(test_example_okinawa)
{
    // The text template
    string text = "I heart {$place}!";
    // Data to feed the template engine
    cpptempl::data_map data;
    // {$place} => Okinawa
    data["place"] = cpptempl::make_data("Okinawa");
    // parse the template with the supplied data dictionary
    string result = cpptempl::parse(text, data);

    string expected = "I heart Okinawa!";
    BOOST_CHECK_EQUAL(result, expected);
}

BOOST_AUTO_TEST_SUITE_END()

// ------------------------------------------------------------------------------------------

BOOST_AUTO_TEST_SUITE(TestCppTemplateMisc)

BOOST_AUTO_TEST_CASE(test_empty_stmt)
{
    string text = "aaa{%  %}bbb{$item}ccc";
    data_map data;
    data["item"] = "xyz";
    BOOST_CHECK_EQUAL(parse(text, data), "aaabbbxyzccc");
}
BOOST_AUTO_TEST_CASE(test_comment_only_stmt)
{
    string text = "aaa{% -- a comment! %}bbb{$item -- a comment on a var}ccc";
    data_map data;
    data["item"] = "xyz";
    BOOST_CHECK_EQUAL(parse(text, data), "aaabbbxyzccc");
}
BOOST_AUTO_TEST_CASE(test_newline_elision)
{
    string text =
        "{% if predicate >%}\n"
        "{$item >}\n"
        "{% endif %}";
    data_map data;
    data["predicate"] = true;
    data["item"] = "foo";
    BOOST_CHECK_EQUAL(parse(text, data), "foo");
}
BOOST_AUTO_TEST_CASE(test_newline_eater)
{
    string text =
        "hello{%>%}\n"
        " world";
    data_map data;
    BOOST_CHECK_EQUAL(parse(text, data), "hello world");
}
BOOST_AUTO_TEST_CASE(test_newline_eater_with_comment)
{
    string text =
        "hello{% -- get rid of following newline >%}\n"
        " world";
    data_map data;
    BOOST_CHECK_EQUAL(parse(text, data), "hello world");
}

BOOST_AUTO_TEST_SUITE_END()

// ------------------------------------------------------------------------------------------

BOOST_AUTO_TEST_SUITE(TestCppTemplateIf)

BOOST_AUTO_TEST_CASE(test_if_false)
{
    string text = "{% if predicate %}{$item}{% endif %}";
    data_map data;
    data["predicate"] = false;
    data["item"] = make_data("foo");
    string actual = parse(text, data);
    string expected = "";
    BOOST_CHECK_EQUAL(expected, actual);
}
BOOST_AUTO_TEST_CASE(test_if_true)
{
    string text = "{% if predicate %}{$item}{% endif %}";
    data_map data;
    data["predicate"] = true;
    data["item"] = make_data("foo");
    string actual = parse(text, data);
    string expected = "foo";
    BOOST_CHECK_EQUAL(expected, actual);
}
BOOST_AUTO_TEST_CASE(test_if_comments)
{
    string text = "{% if item -- an if statement %}{$item}{% endif -- end of if %}";
    data_map data;
    data["item"] = make_data("foo");
    string actual = parse(text, data);
    string expected = "foo";
    BOOST_CHECK_EQUAL(expected, actual);
}
BOOST_AUTO_TEST_CASE(test_nested_if_false)
{
    string text = "{% if item %}{% if thing %}{$item}{$thing}{% endif %}{% endif %}";
    data_map data;
    data["item"] = make_data("aaa");
    data["thing"] = make_data("");
    string actual = parse(text, data);
    string expected = "";
    BOOST_CHECK_EQUAL(expected, actual);
}
BOOST_AUTO_TEST_CASE(test_nested_if_true)
{
    string text = "{% if item %}{% if thing %}{$item}{$thing}{% endif %}{% endif %}";
    data_map data;
    data["item"] = make_data("aaa");
    data["thing"] = make_data("bbb");
    string actual = parse(text, data);
    string expected = "aaabbb";
    BOOST_CHECK_EQUAL(expected, actual);
}
BOOST_AUTO_TEST_CASE(test_usage_example)
{
    string text =
        "{% if item %}{$item}{% endif %}\n"
        "{% if thing %}{$thing}{% endif %}";
    cpptempl::data_map data;
    data["item"] = cpptempl::make_data("aaa");
    data["thing"] = cpptempl::make_data("bbb");

    string result = cpptempl::parse(text, data);

    string expected = "aaa\nbbb";
    BOOST_CHECK_EQUAL(result, expected);
}
BOOST_AUTO_TEST_CASE(test_syntax_if)
{
    string text = "{% if person.name == \"Bob\" %}Full name: Robert{% endif %}";
    data_map person;
    person["name"] = make_data("Bob");
    person["occupation"] = make_data("Plumber");
    data_map data;
    data["person"] = make_data(person);

    string result = cpptempl::parse(text, data);

    string expected = "Full name: Robert";
    BOOST_CHECK_EQUAL(result, expected);
}
BOOST_AUTO_TEST_CASE(test_example_if_else)
{
    // The text template
    string text = "{% if foo %}yes{% else %}no{% endif %}";
    // Data to feed the template engine
    cpptempl::data_map data;
    data["foo"] = true;
    // parse the template with the supplied data dictionary
    BOOST_CHECK_EQUAL(cpptempl::parse(text, data), "yes");

    data["foo"] = false;
    BOOST_CHECK_EQUAL(cpptempl::parse(text, data), "no");
}
BOOST_AUTO_TEST_CASE(test_example_if_elif)
{
    // The text template
    string text = "{% if foo %}aa{% elif bar %}bb{% endif %}";
    // Data to feed the template engine
    cpptempl::data_map data;
    data["foo"] = true;
    data["bar"] = false;
    // parse the template with the supplied data dictionary
    BOOST_CHECK_EQUAL(cpptempl::parse(text, data), "aa");

    data["foo"] = false;
    BOOST_CHECK_EQUAL(cpptempl::parse(text, data), "");

    data["bar"] = true;
    BOOST_CHECK_EQUAL(cpptempl::parse(text, data), "bb");
}
BOOST_AUTO_TEST_CASE(test_example_if_elif_else)
{
    // The text template
    string text = "{% if foo %}aa{% elif bar %}bb{% else %}cc{% endif %}";
    // Data to feed the template engine
    cpptempl::data_map data;
    data["foo"] = true;
    data["bar"] = false;
    // parse the template with the supplied data dictionary
    BOOST_CHECK_EQUAL(cpptempl::parse(text, data), "aa");

    data["foo"] = false;
    BOOST_CHECK_EQUAL(cpptempl::parse(text, data), "cc");

    data["bar"] = true;
    BOOST_CHECK_EQUAL(cpptempl::parse(text, data), "bb");
}
BOOST_AUTO_TEST_CASE(test_example_if_elif_x2)
{
    // The text template
    string text = "{% if foo %}aa{% elif bar %}bb{% elif baz %}cc{% endif %}";
    // Data to feed the template engine
    cpptempl::data_map data;
    data["foo"] = false;
    data["bar"] = false;
    data["baz"] = false;
    // parse the template with the supplied data dictionary
    BOOST_CHECK_EQUAL(cpptempl::parse(text, data), "");

    data["foo"] = true;
    BOOST_CHECK_EQUAL(cpptempl::parse(text, data), "aa");

    data["foo"] = false;
    data["baz"] = true;
    BOOST_CHECK_EQUAL(cpptempl::parse(text, data), "cc");

    data["bar"] = true;
    BOOST_CHECK_EQUAL(cpptempl::parse(text, data), "bb");
}

BOOST_AUTO_TEST_SUITE_END()

// ------------------------------------------------------------------------------------------

BOOST_AUTO_TEST_SUITE(TestCppTemplateFor)

BOOST_AUTO_TEST_CASE(test_for)
{
    string text = "{% for item in items %}{$item}{% endfor %}";
    data_map data;
    data_list items;
    items.push_back(make_data("0"));
    items.push_back(make_data("1"));
    data["items"] = make_data(items);
    string actual = parse(text, data);
    string expected = "01";
    BOOST_CHECK_EQUAL(expected, actual);
}
BOOST_AUTO_TEST_CASE(test_for_loop_vars)
{
    string text =
        "{% for item in items %}\n"
        "index={$loop.index}; index0={$loop.index0}; first={$loop.first}; last={$loop.last}; "
        "even={$loop.even}; odd={$loop.odd}; count={$loop.count}; "
        "addNewLineIfNotLast={$loop.addNewLineIfNotLast}"
        "{% endfor %}";
    data_map data;
    data_list items;
    items.push_back(make_data(1));
    items.push_back(make_data(2));
    items.push_back(make_data(3));
    data["items"] = make_data(items);
    string actual = parse(text, data);
    string expected =
        "index=1; index0=0; first=true; last=false; even=false; odd=true; count=3; addNewLineIfNotLast=\n"
        "index=2; index0=1; first=false; last=false; even=true; odd=false; count=3; addNewLineIfNotLast=\n"
        "index=3; index0=2; first=false; last=true; even=false; odd=true; count=3; addNewLineIfNotLast=";
    BOOST_CHECK_EQUAL(expected, actual);
}
BOOST_AUTO_TEST_CASE(test_nested_for)
{
    string text = "{% for item in items %}{% for thing in things %}{$item}{$thing}{% endfor %}{% endfor %}";
    data_map data;
    data_list items;
    items.push_back(make_data("0"));
    items.push_back(make_data("1"));
    data["items"] = make_data(items);
    data_list things;
    things.push_back(make_data("a"));
    things.push_back(make_data("b"));
    data["things"] = make_data(things);
    string actual = parse(text, data);
    string expected = "0a0b1a1b";
    BOOST_CHECK_EQUAL(expected, actual);
}
BOOST_AUTO_TEST_CASE(test_syntax_dotted)
{
    string text =
        "{% for friend in person.friends %}"
        "{$loop.index}. {$friend.name} "
        "{% endfor %}";

    data_map bob;
    bob["name"] = make_data("Bob");
    data_map betty;
    betty["name"] = make_data("Betty");
    data_list friends;
    friends.push_back(make_data(bob));
    friends.push_back(make_data(betty));
    data_map person;
    person["friends"] = make_data(friends);
    data_map data;
    data["person"] = make_data(person);

    string result = cpptempl::parse(text, data);

    string expected = "1. Bob 2. Betty ";
    BOOST_CHECK_EQUAL(result, expected);
}
BOOST_AUTO_TEST_CASE(test_example_ul)
{
    string text =
        "<h3>Locations</h3><ul>"
        "{% for place in places %}"
        "<li>{$place}</li>"
        "{% endfor %}"
        "</ul>";

    // Create the list of items
    cpptempl::data_list places;
    places.push_back(cpptempl::make_data("Okinawa"));
    places.push_back(cpptempl::make_data("San Francisco"));
    // Now set this in the data map
    cpptempl::data_map data;
    data["places"] = cpptempl::make_data(places);
    // parse the template with the supplied data dictionary
    string result = cpptempl::parse(text, data);
    string expected =
        "<h3>Locations</h3><ul>"
        "<li>Okinawa</li>"
        "<li>San Francisco</li>"
        "</ul>";
    BOOST_CHECK_EQUAL(result, expected);
}
BOOST_AUTO_TEST_CASE(test_example_for_loop_var_restore)
{
    // The text template
    string text =
        "{% for x in items %}.{$loop.index}"
        "{% for y in more %}:{$loop.index}{% endfor %}"
        "-{$loop.index}{% endfor %}";
    // Data to feed the template engine
    data_list items;
    items.push_back("a");
    items.push_back("b");
    data_list more;
    more.push_back("1");
    more.push_back("2");
    more.push_back("3");
    cpptempl::data_map data;
    data["items"] = items;
    data["more"] = more;

    // parse the template with the supplied data dictionary
    BOOST_CHECK_EQUAL(cpptempl::parse(text, data), ".1:1:2:3-1.2:1:2:3-2");
}

BOOST_AUTO_TEST_SUITE_END()

// ------------------------------------------------------------------------------------------

BOOST_AUTO_TEST_SUITE(TestCppTemplateDef)

BOOST_AUTO_TEST_CASE(test_example_def)
{
    // The text template
    string text = "{% def foo %}hello world{% enddef %}{$foo}";
    // Data to feed the template engine
    cpptempl::data_map data;
    // parse the template with the supplied data dictionary
    BOOST_CHECK_EQUAL(cpptempl::parse(text, data), "hello world");
}
BOOST_AUTO_TEST_CASE(test_example_def_param)
{
    // The text template
    string text = "{% def foo(p) %}hello {$p} world{% enddef %}{$foo('happy')}|{$foo('sad')}";
    // Data to feed the template engine
    cpptempl::data_map data;
    // parse the template with the supplied data dictionary
    BOOST_CHECK_EQUAL(cpptempl::parse(text, data), "hello happy world|hello sad world");
}
BOOST_AUTO_TEST_CASE(test_example_multi_def)
{
    string text = "{% def foo(place) %}hello {$place}{% enddef %}";
    data_map data;
    parse(text, data);
    string text2 = "{$foo('world')}";
    BOOST_CHECK_EQUAL(parse(text2, data), "hello world");
}
BOOST_AUTO_TEST_CASE(test_example_multi_def_2)
{
    string text = "{% def foo(place) %}hello {$place}{% enddef %}";
    data_map data;
    parse(text, data);
    {
        data_map items;
        items["bar"] = data["foo"];
        data["items"] = items;
    }
    string text2 = "{$items.bar('world')}";
    BOOST_CHECK_EQUAL(parse(text2, data), "hello world");
}

data_map get_things_map()
{
    data_map data;
    data_list mems;
    data_map x;
    x["name"] = "A";
    x["value"] = "1";
    mems.push_back(x);
    data_map y;
    y["name"] = "B";
    y["value"] = "2";
    mems.push_back(y);
    data_map e;
    e["members"] = mems;
    e["name"] = "letters";
    data_list mems2;
    data_map z;
    z["name"] = "Q";
    z["value"] = "10";
    mems2.push_back(z);
    data_map f;
    f["members"] = mems2;
    f["name"] = "fun";
    data_list es;
    es.push_back(e);
    es.push_back(f);
    data["things"] = es;
    return data;
}
BOOST_AUTO_TEST_CASE(test_example_multi_def_inner_for)
{
    string text =
        "{% def defwithloop(info) %}"
        "({$info.name}:"
        "{% for thing in info.members %}"
        "[{$thing.name}{% if thing.value %}={$thing.value}{% endif%}]"
        "{% endfor %})"
        "{% enddef %}"
        "{% for x in things %}"
        "{$defwithloop(x)}"
        "{% endfor %}";
    data_map data = get_things_map();

    BOOST_CHECK_EQUAL(parse(text, data), "(letters:[A=1][B=2])(fun:[Q=10])");
}
BOOST_AUTO_TEST_CASE(test_example_multi_def_call_def)
{
    string text =
        "{% def outerdef(info) %}({$info.name}:{$defwithloop(info)}){% enddef %}"
        "{% def defwithloop(info) %}"
        "{% for thing in info.members %}"
        "[{$thing.name}{% if thing.value %}={$thing.value}{% endif%}]"
        "{% endfor %}"
        "{% enddef %}"
        "{% for x in things %}"
        "{$outerdef(x)}"
        "{% endfor %}";
    data_map data = get_things_map();

    BOOST_CHECK_EQUAL(parse(text, data), "(letters:[A=1][B=2])(fun:[Q=10])");
}

BOOST_AUTO_TEST_SUITE_END()

// According to the docs this main() should be provided by the boost unit test lib,
// but it wasn't linking until I added it.
int main(int argc, char *argv[])
{
    return boost::unit_test::unit_test_main(init_unit_test, argc, argv);
}

max_stars_repo_name: ErichStyger/erpc

max_issues_repo_name: ErichStyger/erpc

max_forks_repo_name: ErichStyger/erpc


text:
// -----------------------------------------------------------------------------
// handling_errors_ec_version.cpp : examples that show how use
// Boost.Application to make a simplest interactive (terminal) application
//
// Note 1: The Boost.Application (Aspects v4) and this sample are in
//         development process.
// -----------------------------------------------------------------------------

// Copyright 2011-2013 Renato Tegon Forti
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

// -----------------------------------------------------------------------------

#include <iostream>
#include <boost/application.hpp>

using namespace boost;

class myapp {
public:
    // param
    int operator()(application::context &context) {
        return 0;
    }
};

// main

//[ecver
// ...
int main(int argc, char *argv[]) {
    myapp app;
    application::context app_context;

    boost::system::error_code ec;
    int result = application::launch<application::common>(app, app_context, ec);

    if (ec) {
        std::cerr << "[E] " << ec.message() << " <" << ec.value() << "> " << std::endl;
    }

    return result;
}
//]

ext: cpp

max_stars_repo_name: NilFoundation/application

max_issues_repo_name: NilFoundation/application

max_forks_repo_name: NilFoundation/application


text:
//---------------------------------------------------------------------------//
// Copyright (c) 2018-2021 Mikhail Komarov <nemo@nil.foundation>
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//---------------------------------------------------------------------------//

#define BOOST_TEST_MODULE core

#include <boost/test/included/unit_test.hpp>

#include <nil/actor/core/detail/uname.hh>

using namespace nil::actor::detail;

BOOST_AUTO_TEST_CASE(test_nowait_aio_fix) {
    auto check = [](const char *uname) {
        return parse_uname(uname).whitelisted({"5.1", "5.0.8", "4.19.35", "4.14.112"});
    };
    BOOST_REQUIRE_EQUAL(check("5.1.0"), true);
    BOOST_REQUIRE_EQUAL(check("5.1.1"), true);
    BOOST_REQUIRE_EQUAL(check("5.1.1-44.distro"), true);
    BOOST_REQUIRE_EQUAL(check("5.1.1-44.7.distro"), true);
    BOOST_REQUIRE_EQUAL(check("5.0.0"), false);
    BOOST_REQUIRE_EQUAL(check("5.0.7"), false);
    BOOST_REQUIRE_EQUAL(check("5.0.7-55.el19"), false);
    BOOST_REQUIRE_EQUAL(check("5.0.8"), true);
    BOOST_REQUIRE_EQUAL(check("5.0.9"), true);
    BOOST_REQUIRE_EQUAL(check("5.0.8-200.fedora"), true);
    BOOST_REQUIRE_EQUAL(check("5.0.9-200.fedora"), true);
    BOOST_REQUIRE_EQUAL(check("5.2.0"), true);
    BOOST_REQUIRE_EQUAL(check("5.2.9"), true);
    BOOST_REQUIRE_EQUAL(check("5.2.9-77.el153"), true);
    BOOST_REQUIRE_EQUAL(check("6.0.0"), true);
    BOOST_REQUIRE_EQUAL(check("3.9.0"), false);
    BOOST_REQUIRE_EQUAL(check("4.19"), false);
    BOOST_REQUIRE_EQUAL(check("4.19.34"), false);
    BOOST_REQUIRE_EQUAL(check("4.19.35"), true);
    BOOST_REQUIRE_EQUAL(check("4.19.36"), true);
    BOOST_REQUIRE_EQUAL(check("4.20.36"), false);
    BOOST_REQUIRE_EQUAL(check("4.14.111"), false);
    BOOST_REQUIRE_EQUAL(check("4.14.112"), true);
    BOOST_REQUIRE_EQUAL(check("4.14.113"), true);
}

BOOST_AUTO_TEST_CASE(test_xfs_concurrency_fix) {
    auto check = [](const char *uname) { return parse_uname(uname).whitelisted({"3.15", "3.10.0-325.el7"}); };
    BOOST_REQUIRE_EQUAL(check("3.15.0"), true);
    BOOST_REQUIRE_EQUAL(check("5.1.0"), true);
    BOOST_REQUIRE_EQUAL(check("3.14.0"), false);
    BOOST_REQUIRE_EQUAL(check("3.10.0"), false);
    BOOST_REQUIRE_EQUAL(check("3.10.14"), false);
    BOOST_REQUIRE_EQUAL(check("3.10.0-325.ubuntu"), false);
    BOOST_REQUIRE_EQUAL(check("3.10.0-325"), false);
    BOOST_REQUIRE_EQUAL(check("3.10.0-325.el7"), true);
    BOOST_REQUIRE_EQUAL(check("3.10.0-326.el7"), true);
    BOOST_REQUIRE_EQUAL(check("3.10.0-324.el7"), false);
    BOOST_REQUIRE_EQUAL(check("3.10.0-325.665.el7"), true);
}

ext: cc

max_stars_repo_name: NilFoundation/actor-core

max_issues_repo_name: NilFoundation/actor-core

max_forks_repo_name: NilFoundation/actor-core


text:
// Copyright Abel Sinkovics (abel@sinkovics.hu)  2013.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#include <process/impl/output_file.hpp>

#include "util.hpp"

#include <boost/test/unit_test.hpp>

#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

#include <string>
#include <fstream>
#include <iostream>
#include <sstream>

using namespace process::impl;

namespace
{
  int open_temp_file()
  {
    return
      open(
        temp_filename.c_str(),
        O_CREAT | O_WRONLY | O_TRUNC,
        S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
      );
  }
}

BOOST_AUTO_TEST_CASE(test_destructor_closing_output_file)
{
  const int fd = open_temp_file();
  {
    output_file f(fd);
  }

  BOOST_CHECK(!file_descriptor_is_open(fd));
}

BOOST_AUTO_TEST_CASE(test_write_to_output_file)
{
  output_file f(open_temp_file());
  f.write("hello", 5);

  BOOST_CHECK_EQUAL("hello", read_file(temp_filename));
}

BOOST_AUTO_TEST_CASE(test_close_output_file)
{
  const int fd = open_temp_file();
  output_file f(fd);
  f.close();

  BOOST_CHECK(!file_descriptor_is_open(fd));
}


text:
/**
 * @file ekfSlam.hpp
 * @brief EKF for implementing SLAM using data from viodom
 * @author Manas Mehta, mmehta@wpi.edu
 * @date July 2021
 *
 */

#ifndef __EKFSLAM_H__
#define __EKFSLAM_H__

#include <tf_conversions/tf_eigen.h>
#include <Eigen/Core>
#include <Eigen/Dense>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <visor/kpt.h>
#include <cv_bridge/cv_bridge.h>
#include <image_transport/image_transport.h>
#include <sensor_msgs/PointCloud.h>
#include <sensor_msgs/PointCloud2.h>
#include <sensor_msgs/point_cloud_conversion.h>
#include <sensor_msgs/Image.h>
#include "stereodom.hpp"
#include "robustmatcher.hpp"
#include "landmark.hpp"

// Convenient constants
#define RAD2DEG(X) ( (X) * 57.2957795131)
#define DEG2RAD(X) ( (X) * 0.01745329251)
#define LIN2GRAV(X) ( (X) * 0.10197162)

/**
 * @brief Keypoint comparison auxiliar function to sort the sets of keypoints
 * according to their score
 * @param p1 The first keypoint
 * @param p2 The second keypoint
 * @return Boolean true if p1 > p2
 */
bool score_comparator1(const cv::KeyPoint& p1, const cv::KeyPoint& p2) {
    return p1.response > p2.response;
}

/**
 * @class EkfSlam
 * @brief calculates robot's pose and simultaneously creates a map of the environment
 */
class EkfSlam {

public:

    /** @brief Constructor
     * @param T Prediction period (seconds)
     * @param poseTopic Name of the IMU data topic
     * @param calibTime Initial calibration time (seconds)
     */
    EkfSlam(std::string& nodeName, std::string& kptTopic) {
        init_ = false;

        fDetector_ = cv::FastFeatureDetector::create(/*5*/);
        fExtractor_ = cv::xfeatures2d::BriefDescriptorExtractor::create(16 /*32*/);

        // initialize hyperparameters
        downsampling_ = 2;
        maxFeatures_ = 30;

        // Setup data subscribers
        kptSub_ = nh_.subscribe(kptTopic, 10, &EkfSlam::kptDataCallback, this);

        // Setup data publishers
        posePub_ = nh_.advertise<geometry_msgs::TransformStamped>(nodeName + "/slamPose", 1);
        pcPub_ = nh_.advertise<sensor_msgs::PointCloud2>(nodeName + "/point_cloud", 1);

        deltaT_ = 1.0;
        simTh_ = 1.0;

        // Init cam2baselink TFs
        Tcam22imu0_.setOrigin(tf::Vector3(-0.039, 0.0089, 0.0009));
        Tcam22imu0_.setRotation(tf::Quaternion(0.00065, 0.0014, -0.0031, 0.9999));
        Timu02base_.setOrigin(tf::Vector3(0.12, 0.0, -0.06));
        tf::Quaternion q_imu0;
        q_imu0.setRPY(-1.96, 0.0, -1.57);
        Timu02base_.setRotation(q_imu0);
	}

    /** @brief Initialize EKF
     * Input: visor::kpt::ConstPtr keyframe
     * The user must continue calling this function with new gyro data until it returns true
     */
	bool initialize(const visor::kpt::ConstPtr& msg) {

		// Initialize state vector CV_
        SV_ = std::vector<double>(314, 0.0);

        // Initialize covariance matrices
        CM_.setIdentity(314, 314);
        CM_.setZero();
        Q_.setIdentity(314, 314);
        Q_.setZero();
        R_.setIdentity(314, 314);
        R_.setZero();

        if(msg) {
            sensor_msgs::CameraInfo leftInfo, rightInfo;

            leftInfo = msg->infoL;
            rightInfo = msg->infoR;

            calibInit(leftInfo, rightInfo);
        }

        numLandmarks_ = 0;

        init_ = true;
        ROS_INFO("SLAM EKF INITIALIZED");

        observedL_ = false;

        return true;
	}

    /** @brief generate landmark with global location for the map using the current observed landmarks
     * @param[in] landmarkC current observed landmark
     * @return landmark for storing in the map
     */
    Landmark mapLandmarkCreator(Landmark& landmarkC) {

        // get relative landmark location
        cv::Point3f rp;
        rp.x = landmarkC.getLocRel().x;
        rp.y = landmarkC.getLocRel().y;
        rp.z = landmarkC.getLocRel().z;

        // set global landmark location as gp = relative location + predicted pose
        cv::Point3f gp;
        gp.x = rp.x + SV_[0];
        gp.y = rp.y + SV_[1];
        gp.z = rp.z + SV_[2];

        return Landmark(landmarkC.getKpt(), rp, gp);
    }

    /** @brief Distribute maxFeatures_ among all buckets.
     * We are considering 6 buckets organized in 2 rows and 3 columns.
     * @param[in] srcKpts Sorted key-points
     * @param[out] dstKpts Output bucketed key-points
     * @param[in] width Image width (cols)
     * @param[in] height Image height (rows)
     */
    void kptsBucketing(std::vector<cv::KeyPoint>& srcKpts, std::vector<cv::KeyPoint>& dstKpts,
        int width, int height) {
        const int maxFeatBuck = maxFeatures_ / 6;
        int buckets[6] = { maxFeatBuck, maxFeatBuck, maxFeatBuck, maxFeatBuck, maxFeatBuck, maxFeatBuck };
        for (size_t i = 0; i < srcKpts.size(); i++) {
            int id;

            // Check keypoint y coord
            if (srcKpts[i].pt.y <= height / 2)
                id = 0;
            else
                id = 3;

            // Check keypoint x coord
            if (srcKpts[i].pt.x <= width / 3)
                id += 0;
            else if (srcKpts[i].pt.x <= 2 * width / 3)
                id += 1;
            else
                id += 2;

            // Assign to corresponding bucket
            if (buckets[id] > 0) {
                buckets[id]--;
                dstKpts.push_back(srcKpts[i]);
            }
        }
    }

    /** EKF prediction stage based on pose data from viodom
     * @param[in] pose Predicted pose from viodom
     * @param[in] dist Pose change between previous and current frame calculated using PnP-RANSAC
     */
	bool predict(tf::Transform pose, tf::Transform dist) {

        // ut/t-1 = fv(ut-1/t-1, ct, nt)

        // get deltaT
        ros::Time timeC = ros::Time::now();
        deltaT_ = (timeC - timeP_).toSec();
        timeP_ = timeC;

        tf::Quaternion qPrev;
        qPrev.setW(SV_[3]);
        qPrev.setX(SV_[4]);
        qPrev.setY(SV_[5]);
        qPrev.setZ(SV_[6]);
        tf::Vector3 pPrev;
        pPrev.setX(SV_[0]);
        pPrev.setY(SV_[1]);
        pPrev.setZ(SV_[2]);

        poseP_.setRotation(qPrev);
        poseP_.setOrigin(pPrev);

        // pose translation vector
        SV_[0] = pose.getOrigin().getX();
        SV_[1] = pose.getOrigin().getY();
        SV_[2] = pose.getOrigin().getZ();

        // pose rotation quaternion
        SV_[3] = pose.getRotation().getW();
        SV_[4] = pose.getRotation().getX();
        SV_[5] = pose.getRotation().getY();
        SV_[6] = pose.getRotation().getZ();

        // linear velocity vector
        SV_[7] = pose.getOrigin().getX() / deltaT_;
        SV_[8] = pose.getOrigin().getY() / deltaT_;
        SV_[9] = pose.getOrigin().getZ() / deltaT_;

        // angular velocity quaternion
        SV_[10] = pose.getRotation().getW() / deltaT_;
        SV_[11] = pose.getRotation().getX() / deltaT_;
        SV_[12] = pose.getRotation().getY() / deltaT_;
        SV_[13] = pose.getRotation().getZ() / deltaT_;

        // Gt = g(ct)
        // motion jacobian
        Eigen::MatrixXd Gt;
        Gt.setIdentity(14, 16);
        Gt.setZero();

        // (0,0) jacobian = r/r
        Eigen::MatrixXd tempI4;
        tempI4.setIdentity(4, 4);

        Gt.block<4,4>(0,0) = tempI4;

        // jacobians for (1,0) = q/r, (2,0) = v/r, (3,0) = w/r are 0

        // jacobian for (0,1) = r/q
        Eigen::Matrix3d qkk;
        Eigen::Vector3d rkk;

        tf::matrixTFToEigen(dist.getBasis(), qkk);
        tf::vectorTFToEigen(dist.getOrigin(), rkk);

        Eigen::MatrixXd hkk;
        hkk.setIdentity(4, 4);
        hkk.block<3, 3>(0,0) = qkk;
        hkk.block<3, 1>(0,3) = rkk * (-1);

        Gt.block<4,4>(0,4) = hkk;

        // jacobian for (1,1) = q/q
        Gt.block<3,3>(4,4) = qkk;

        // jacobian for (2,1) = v/q
        Gt.block<4,4>(7,4) = hkk / deltaT_;

        // jacobian for (3,1) = w/q = 0

        // jacobian for (0,2) = r/v
        Gt.block<4,4>(0,8) = tempI4 * deltaT_;

        // jacobian for (1,2) = q/v
        Eigen::MatrixXd diagr;
        diagr.setIdentity(3, 3);
        diagr(0,0) = dist.getOrigin().getX();
        diagr(1,1) = dist.getOrigin().getY();
        diagr(2,2) = dist.getOrigin().getZ();

        Gt.block<3,3>(4,8) = diagr * deltaT_;

        // jacobian for (2,2) = v/v
        Gt.block<4,4>(7,8) = tempI4;

        // jacobian for (3,2) = w/v
        Eigen::Matrix3d qOld;

        tf::matrixTFToEigen(poseP_.getBasis(), qOld);

        Eigen::MatrixXd holdkk;
        holdkk.setZero(3, 4);
        holdkk.block<3, 3>(0,0) = qOld.inverse();
        holdkk.block<3, 1>(0,3) = rkk;

        Gt.block<3,4>(11,8) = holdkk;

        // jacobian for (0,3) = r/w
        Eigen::MatrixXd hokkT;
        hokkT.setIdentity(4, 4);
        hokkT.block<3, 3>(0,0) = qOld;
        hokkT.block<3, 1>(0,3) = rkk * (-1);

        Gt.block<4,4>(0,12) = hokkT * deltaT_;

        // jacobian for (1,3) = q/w
        Gt.block<3,3>(4,12) = qOld * deltaT_;

        // jacobian for (2,3) = v/w
        Gt.block<4,4>(7,12) = hokkT;

        // jacobian for (3,3) = w/w
        Eigen::MatrixXd tempI3;
        tempI3.setIdentity(3, 3);

        Gt.block<3,3>(11,12) = tempI3;

        // transform matrix for jacobian-covariance multiplication
        Eigen::MatrixXd tr;
        tr.setIdentity(314, 314);
        tr.block<14,16>(0,0) = Gt;

        // temporary bias matrices
        Eigen::MatrixXd B1, B2;
        B1.setIdentity(14, 314);
        B2.setIdentity(314, 14);

        // add random bias to the bias matrices
        std::srand(std::time(0));

        for(int i = 0; i < 14; i++) {
            for(int j = 0; j < 314; j++) {
                B1(i,j) = std::rand() % 100 + 1;
                B2(j,i) = std::rand() % 100 + 1;
            }
        }

        // add bias to bias matrix Q
        Q_.block<14,314>(0,0) = B1;
        Q_.block<314,14>(0,0) = B2;

        // ROS_INFO_STREAM("CM (" << CM_(0,0) << "," << CM_(0,313) << "," << CM_(313,313) << "," << CM_(313,0) << ")");

        // update covariance matrix
        CM_ =  tr * CM_ * tr.transpose() + Q_;

        ROS_INFO_STREAM("State Vector: new pose (" << SV_[0] << "," << SV_[1] << "," << SV_[2] << ","<< ")");
        // ROS_INFO_STREAM("CM(" << CM_(0,0) << "," << CM_(0,313) << "," << CM_(313,313) << "," << CM_(313,0) << ")");

        return true;
	}

    /** EKF update stage
     */
	bool update() {

        // iterate through every observed landmark
        for(std::vector<Landmark>::iterator lmc = lmrksC_.begin(); lmc != lmrksC_.end(); ++lmc) {

            // index of current landmark in the map and the covariance matrix
            int lmIndex = 0;

            // initialize landmark for the map
            Landmark nlm = mapLandmarkCreator((*lmc));

            // if map is empty, add the first landmark
            if(numLandmarks_ == 0) {

                // add landmark to the map
                map.push_back(nlm);

                // add landmark to state vector
                SV_[14] = nlm.getLocGlob().x;
                SV_[15] = nlm.getLocGlob().y;
                SV_[16] = nlm.getLocGlob().z;

                numLandmarks_ = 1;

            } else {

                double minEucDist = 10000.0;
                Landmark minLm = nlm;
                int minIndex = 0;
                int index = 0;

                // iterate through every landmark stored in the map
                for(std::vector<Landmark>::iterator lmm = map.begin(); lmm != map.end(); ++lmm) {

                    // calculate euclidean distance between points
                    double eucDist = std::sqrt((nlm.getLocGlob().x - (*lmm).getLocGlob().x)*(nlm.getLocGlob().x - (*lmm).getLocGlob().x)
                                                 + (nlm.getLocGlob().y - (*lmm).getLocGlob().y)*(nlm.getLocGlob().y - (*lmm).getLocGlob().y)
                                                 + (nlm.getLocGlob().z - (*lmm).getLocGlob().z)*(nlm.getLocGlob().z - (*lmm).getLocGlob().z));

                    // get min euclidean distance and corresponding landmark
                    if(eucDist < minEucDist) {
                        minEucDist = eucDist;
                        minLm = (*lmm);
                        minIndex = index;
                    }

                    index += 1;
                }

                // landmark observed before
                if (minEucDist < simTh_) {

                    lmIndex = minIndex;

                    cv::Point3f minLmGL;
                    minLmGL.x = minLm.getLocGlob().x;
                    minLmGL.y = minLm.getLocGlob().y;
                    minLmGL.z = minLm.getLocGlob().z;

                    // update global location of current landmark with global location from the map
                    nlm.setLocGlob(minLmGL);

                } else {

                    // unobserved landmark - add to the map
                    map.push_back(nlm);
                    lmIndex = numLandmarks_;

                    numLandmarks_ += 1;
                }

                // add landmark to the state vector
                SV_[lmIndex * 3 + 14] = nlm.getLocGlob().x;
                SV_[(lmIndex * 3 + 14) + 1] = nlm.getLocGlob().y;
                SV_[(lmIndex * 3 + 14) + 2] = nlm.getLocGlob().z;

                ROS_INFO_STREAM("Yeetus(" << numLandmarks_ << ")");

                // lmIndex = location of current landmark in the map
                // lmIndex * 3 + 14 = location of current landmark in SV and CM

                // observation model
                cv::Point3f h;

                h.x = nlm.getLocGlob().x - SV_[0];
                h.y = nlm.getLocGlob().y - SV_[1];
                h.z = nlm.getLocGlob().z - SV_[2];

                // observation jacobian
                Eigen::MatrixXd H, Hb, Kg, Bi, Qinv, I, SVM, HV;
                H.setIdentity(3,3);
                Bi.setZero(314,314);
                Hb.setZero(314,314);
                I.setIdentity(314,314);
                SVM.setZero(314,1);
                HV.setZero(314,1);

                HV(0,0) = h.x;
                HV(1,0) = h.y;
                HV(2,0) = h.z;

                Qinv = (Hb * CM_ * Hb.transpose()) + Bi;

                // kalman gain
                Kg = CM_ * Hb.transpose() * Qinv.inverse();

                // state vector update
                SVM += Kg * HV;

                for(int i = 0; i < 314; i++) {
                    SV_[i] = SVM(i,0);
                }

                // covariance matrix update
                CM_ = ( I - Kg * Hb ) * CM_;
            }
        }

        return true;
	}

    /** pose data callback
     * @param[in] msg TransformStamped data message
     */
	void kptDataCallback(const visor::kpt::ConstPtr& msg) {

        if(!init_) {
            initialize(msg);
        }

        if(msg) {
            ROS_INFO("KEYPOINT RECEIVED");

            // Get pose data
            geometry_msgs::TransformStamped uPose;
            uPose = msg->pose;

            tf::Quaternion qPose;
            qPose.setX(uPose.transform.rotation.x);
            qPose.setY(uPose.transform.rotation.y);
            qPose.setZ(uPose.transform.rotation.z);
            qPose.setW(uPose.transform.rotation.w);
            tf::Vector3 pPose;
            pPose.setX(uPose.transform.translation.x);
            pPose.setY(uPose.transform.translation.y);
            pPose.setZ(uPose.transform.translation.z);
            tf::Transform odomTemp;
            odomTemp.setRotation(qPose);
            odomTemp.setOrigin(pPose);

            // Get displacement data
            geometry_msgs::TransformStamped uDist;
            uDist = msg->dist;

            tf::Quaternion qDist;
            qDist.setX(uDist.transform.rotation.x);
            qDist.setY(uDist.transform.rotation.y);
            qDist.setZ(uDist.transform.rotation.z);
            qDist.setW(uDist.transform.rotation.w);
            tf::Vector3 pDist;
            pDist.setX(uDist.transform.translation.x);
            pDist.setY(uDist.transform.translation.y);
            pDist.setZ(uDist.transform.translation.z);
            tf::Transform distTemp;
            distTemp.setRotation(qDist);
            distTemp.setOrigin(pDist);

		    // ekf predict step
		    predict(odomTemp, distTemp);

            // get observations - left and right images
            generateLandmarks(msg->imgL, msg->imgR);

            // ekf update step
            update();

            // send corrected pose to viodom node
            tf::Transform updatedOdomC;

            tf::Quaternion qUpdated;
            qUpdated.setW(SV_[3]);
            qUpdated.setX(SV_[4]);
            qUpdated.setY(SV_[5]);
            qUpdated.setZ(SV_[6]);
            tf::Vector3 pUpdated;
            pUpdated.setX(SV_[0]);
            pUpdated.setY(SV_[1]);
            pUpdated.setZ(SV_[2]);

            updatedOdomC.setRotation(qUpdated);
            updatedOdomC.setOrigin(pUpdated);

            publishSlamTf(updatedOdomC);

        } else {
            ROS_WARN("No keyframe received!");
        }
    }

    /** @brief generate landmarks from left and right images
     * @param leftImg Left image
     * @param rightImg Right image
     */
    void generateLandmarks(const sensor_msgs::Image& leftImg, const sensor_msgs::Image& rightImg) {
        double flow = 0.0;
        lmrksC_.clear();

        // Convert to OpenCV format and rectify both images
        cv::Mat imgL, imgR;
        if (!convertRectifyImages(leftImg, rightImg, imgL, imgR))
            return;

        cv::imshow("Left Image", imgL);
        cv::imshow("Right Image", imgR);
        cv::waitKey(3);

        // Detect key-points in the images
        kptsLeftC_.clear();
        kptsRightC_.clear();
        selectKeypoints(imgL, imgR, kptsLeftC_, kptsRightC_);
        // std::cout << "Keypoints: left (" << kptsLeftC_.size() << "), right (" << kptsRightC_.size() << ")\n";

        // Extract feature descriptors
        extractDescriptors(imgL, imgR, kptsLeftC_, kptsRightC_, descLeftC_, descRightC_);

        // Stereo matching and 3D point cloud generation
        pclC_.clear();
        stereoMatchesC_.clear();
        if (!stereoMatching(kptsLeftC_, kptsRightC_, descLeftC_, descRightC_, stereoMatchesC_, pclC_))
            return;
        //std::cout << "Stereo matches: " stereoMatchesC_.size() << std::endl;

        publishPointCloud(pclC_, leftImg.header);
    }

    /** @brief Publish 3D point cloud
     * @param[in] header Header for the point cloud message
     */
    void publishPointCloud(const std::vector<cv::Point3f> pcl, const std_msgs::Header header)
    {
        // Fill PointCloud message
        sensor_msgs::PointCloud outCloud;
        outCloud.points.resize(pcl.size());
        outCloud.header = header;
        for (size_t i = 0; i < outCloud.points.size(); i++) {
            outCloud.points[i].x = pcl[i].x;
            outCloud.points[i].y = pcl[i].y;
            outCloud.points[i].z = pcl[i].z;
        }

        // Convert PointCloud to PointCloud2
        sensor_msgs::PointCloud2 outCloud2;
        sensor_msgs::convertPointCloudToPointCloud2(outCloud, outCloud2);

        // Publish point cloud
        pcPub_.publish(outCloud2);
    }

    /** @brief Publish 6DOF updated pose as TF and geometry_msgs::TransformStamped
     * @param odom Odometry transform to be published
     */
    void publishSlamTf(const tf::Transform& odom) {

        // // Publish TF
        // tfBr_.sendTransform(
        //     tf::StampedTransform(odom, ros::Time::now(),
        //         srcFrameId_, tgtFrameId_));

        // Publish pose
        geometry_msgs::TransformStamped outTransform;
        outTransform.header.frame_id = "world";
        outTransform.header.stamp = ros::Time::now();
        tf::Quaternion qPose = odom.getRotation();
        outTransform.transform.rotation.x = qPose.x();
        outTransform.transform.rotation.y = qPose.y();
        outTransform.transform.rotation.z = qPose.z();
        outTransform.transform.rotation.w = qPose.w();
        tf::Vector3 pPose = odom.getOrigin();
        outTransform.transform.translation.x = pPose.x();
        outTransform.transform.translation.y = pPose.y();
        outTransform.transform.translation.z = pPose.z();
        posePub_.publish(outTransform);
    }

    /** @brief Converts images to OpenCV format and rectify both images
     * @param[in] leftImg Left image in ROS format
     * @param[in] rightImg Right image in ROS format
     * @param[out] imgL Rectified left image in OpenCV format
     * @param[out] imgR Rectified right image in OpenCV format
     */
    bool convertRectifyImages(const sensor_msgs::Image& leftImg, const sensor_msgs::Image& rightImg, cv::Mat& imgL, cv::Mat& imgR) {
        // Convert to OpenCV format
        cv_bridge::CvImageConstPtr cvbLeft, cvbRight;

        try {
            cvbLeft = cv_bridge::toCvCopy(leftImg);
            cvbRight = cv_bridge::toCvCopy(rightImg);
        } catch (cv_bridge::Exception& e) {
            ROS_ERROR("cv_bridge exception: %s", e.what());
            return false;
        }

        // Rectify both images
        cv::remap(cvbLeft->image, imgL, mapL1_, mapL2_, cv::INTER_LINEAR);
        cv::remap(cvbRight->image, imgR, mapR1_, mapR2_, cv::INTER_LINEAR);
        return true;
    }

    /** @brief Detects key-points in the images
     * Key-points are sorted based on their score to get the best maxFeatures_
     * Features are included in buckets in order to force sparseness
     * @param[in] imgLeft Left image
     * @param[in] imgRight Right image
     * @param[out] keypointsLeft Key-points extracted from left image
     * @param[out] keypointsRight Key-points extracted from right image
     */
    void selectKeypoints(cv::Mat& imgLeft, cv::Mat& imgRight,
        std::vector<cv::KeyPoint>& keypointsLeft,
        std::vector<cv::KeyPoint>& keypointsRight)
    {
        // Detect key-points in both images
        std::vector<cv::KeyPoint> kptsL, kptsR;
        fDetector_->detect(imgLeft, kptsL);
        fDetector_->detect(imgRight, kptsR);

        // Sort keypoints according to their score
        std::sort(kptsL.begin(), kptsL.end(), score_comparator1);
        std::sort(kptsR.begin(), kptsR.end(), score_comparator1);

        // Distribute maxFeatures_ in buckets
        const int width = imgLeft.cols;
        const int height = imgLeft.rows;
        kptsBucketing(kptsL, keypointsLeft, width, height);
        kptsBucketing(kptsR, keypointsRight, width, height);

        // keypointsLeft = kptsL;
        // keypointsRight = kptsR;
    }

    /** @brief Extract feature descriptors from image key-points
     * @param[in] imgLeft Left image
     * @param[in] imgRight Right image
     * @param[in] keypointsLeft Key-points extracted from left image
     * @param[in] keypointsRight Key-points extracted from right image
     * @param[out] descriptorsLeft Feature descriptors for key-points from left image
     * @param[out] descriptorsRight Feature descriptors for key-points from right image
     */
    void extractDescriptors(cv::Mat& imgL, cv::Mat& imgR,
        std::vector<cv::KeyPoint>& keypointsLeft,
        std::vector<cv::KeyPoint>& keypointsRight,
        cv::Mat& descriptorsLeft, cv::Mat& descriptorsRight)
    {
        fExtractor_->compute(imgL, keypointsLeft, descriptorsLeft);
        fExtractor_->compute(imgR, keypointsRight, descriptorsRight);
    }

    /** @brief Match key-points L-R descriptors and generate 3D point cloud
     * @param[in] keypointsLeft Key-points extracted from left image
     * @param[in] keypointsRight Key-points extracted from right image
     * @param[in] descriptorsLeft Feature descriptors for key-points from left image
     * @param[in] descriptorsRight Feature descriptors for key-points from right image
     * @param[out] stereoMatches Resulting matches between features
     * @param[out] pcl Resulting 3D point cloud based on stereo matches between features
     */
    bool stereoMatching(std::vector<cv::KeyPoint>& keypointsLeft,
        std::vector<cv::KeyPoint>& keypointsRight,
        cv::Mat& descriptorsLeft, cv::Mat& descriptorsRight,
        std::vector<cv::DMatch>& stereoMatches, std::vector<cv::Point3f>& pcl)
    {
        std::vector<cv::DMatch> matches;
        try {
            matcher_.match(keypointsRight, descriptorsRight, keypointsLeft, descriptorsLeft,
                matches, false);
        } catch (std::exception e) {
            ROS_ERROR("Matching error!");
            return false;
        }

        // Generate stereo point cloud
        float f = PR_.at<double>(0, 0), cx = PR_.at<double>(0, 2), cy = PR_.at<double>(1, 2), b = -PR_.at<double>(0, 3) / f;
        float d, k1 = 1 / f, k2 = f * b;
        for (size_t i = 0; i < matches.size(); i++) {
            const cv::DMatch& match = matches[i];
            const cv::Point2f& pL = keypointsLeft[match.trainIdx].pt;
            const cv::Point2f& pR = keypointsRight[match.queryIdx].pt;
            d = pL.x - pR.x;
            // If key-points y-coords are close enough (stabilized in roll,pitch)
            // and x-coords are far enough (sufficient disparity), it's a match!
            if (fabs(pR.y - pL.y) <= 5 && d > 2.0) {
                stereoMatches.push_back(match);

                // Calculate 3D point
                cv::Point3f p;
                p.z = k2 / d;
                p.x = (pL.x - cx) * p.z * k1;
                p.y = (pL.y - cy) * p.z * k1;
                pcl.push_back(p);

                cv::Point3f gp;
                gp.x = 0;
                gp.y = 0;
                gp.z = 0;

                lmrksC_.push_back(Landmark(keypointsLeft[match.trainIdx], p, gp));
            }
        }

        // ROS_INFO_STREAM("Number of landmarks (" << lmrksC_.size() << ")");
        // ROS_INFO_STREAM("landmark kpt (" << lmrksC_[0].getKpt().pt << ")");
        // ROS_INFO_STREAM("landmark location (" << lmrksC_[0].getLocRel() << ")");
        // ROS_INFO_STREAM("3D landmark location (" << lmrksC_[0].getLocGlob() << ")");

        return true;
    }

    /** @brief Convert odometry from camera frame to the robot base_link frame
     * @param odomC Odometry transform in camera frame
     * @return Odometry transform in the robot base_link frame
     */
    tf::Transform camera2baselink(const tf::Transform& odomC) {
        tf::Transform odom = Timu02base_ * Tcam22imu0_ * odomC * Tcam22imu0_.inverse() * Timu02base_.inverse();
        return odom;
    }

    /** @brief Convert odometry from the robot base_link frame to camera frame
     * @param odom Odometry transform in base_link frame
     * @return Odometry transform in camera frame
     */
    tf::Transform baselink2camera(const tf::Transform& odom) {
        tf::Transform odomC = Tcam22imu0_.inverse() * Timu02base_.inverse() * odom * Timu02base_ * Tcam22imu0_;
        return odomC;
    }

    /** @brief Camera calibration
     * @param leftInfo Left camera calibration data
     * @param rightInfo Right camera calibration data
     */
    bool calibInit(const sensor_msgs::CameraInfo& leftInfo, const sensor_msgs::CameraInfo& rightInfo) {

        try {

            RL_ = cv::Mat(3, 3, CV_64FC1, (void*)leftInfo.R.elems).clone();
            PL_ = cv::Mat(3, 4, CV_64FC1, (void*)leftInfo.P.elems).clone();
            RR_ = cv::Mat(3, 3, CV_64FC1, (void*)rightInfo.R.elems).clone();
            PR_ = cv::Mat(3, 4, CV_64FC1, (void*)rightInfo.P.elems).clone();

            // Obtain the corresponding PL and PR of the downsampled images
            PL_.at<double>(0, 0) = PL_.at<double>(0, 0) / (float)downsampling_;
            PL_.at<double>(0, 2) = PL_.at<double>(0, 2) / (float)downsampling_;
            PL_.at<double>(0, 3) = PL_.at<double>(0, 3) / (float)downsampling_;
            PL_.at<double>(1, 1) = PL_.at<double>(1, 1) / (float)downsampling_;
            PL_.at<double>(1, 2) = PL_.at<double>(1, 2) / (float)downsampling_;
            PR_.at<double>(0, 0) = PR_.at<double>(0, 0) / (float)downsampling_;
            PR_.at<double>(0, 2) = PR_.at<double>(0, 2) / (float)downsampling_;
            PR_.at<double>(0, 3) = PR_.at<double>(0, 3) / (float)downsampling_;
            PR_.at<double>(1, 1) = PR_.at<double>(1, 1) / (float)downsampling_;
            PR_.at<double>(1, 2) = PR_.at<double>(1, 2) / (float)downsampling_;

            // Initialize left and right image remapping (rectification and undistortion)
            cv::initUndistortRectifyMap(cv::Mat(3, 3, CV_64FC1, (void*)leftInfo.K.elems),
                cv::Mat(4, 1, CV_64FC1, (void*)leftInfo.D.data()),
                RL_, PL_,
                cv::Size(leftInfo.width / (float)downsampling_, leftInfo.height / (float)downsampling_),
                CV_16SC2, mapL1_, mapL2_);
            cv::initUndistortRectifyMap(cv::Mat(3, 3, CV_64FC1, (void*)rightInfo.K.elems),
                cv::Mat(4, 1, CV_64FC1, (void*)rightInfo.D.data()),
                RR_, PR_,
                cv::Size(rightInfo.width / (float)downsampling_, rightInfo.height / (float)downsampling_),
                CV_16SC2, mapR1_, mapR2_);

            // Store single camera calibration after rectification (no distortion)
            KL_ = cv::Mat(3, 3, CV_64FC1);
            KL_.at<double>(0, 0) = PL_.at<double>(0, 0);
            KL_.at<double>(0, 1) = PL_.at<double>(0, 1);
            KL_.at<double>(0, 2) = PL_.at<double>(0, 2);
            KL_.at<double>(1, 0) = PL_.at<double>(1, 0);
            KL_.at<double>(1, 1) = PL_.at<double>(1, 1);
            KL_.at<double>(1, 2) = PL_.at<double>(1, 2);
            KL_.at<double>(2, 0) = PL_.at<double>(2, 0);
            KL_.at<double>(2, 1) = PL_.at<double>(2, 1);
            KL_.at<double>(2, 2) = PL_.at<double>(2, 2);
            KR_ = cv::Mat(3, 3, CV_64FC1);
            KR_.at<double>(0, 0) = PR_.at<double>(0, 0);
            KR_.at<double>(0, 1) = PR_.at<double>(0, 1);
            KR_.at<double>(0, 2) = PR_.at<double>(0, 2);
            KR_.at<double>(1, 0) = PR_.at<double>(1, 0);
            KR_.at<double>(1, 1) = PR_.at<double>(1, 1);
            KR_.at<double>(1, 2) = PR_.at<double>(1, 2);
            KR_.at<double>(2, 0) = PR_.at<double>(2, 0);
            KR_.at<double>(2, 1) = PR_.at<double>(2, 1);
            KR_.at<double>(2, 2) = PR_.at<double>(2, 2);

            ROS_INFO("Slam node calibration succeded!");
            return true;

        } catch (...) {
            ROS_WARN("Slam node calibration failed!");
            return false;
        }

    }

    std::vector<cv::KeyPoint> kptsLeftC_, kptsRightC_;              /**< Stored current (C) keypoints for left and right images*/
    cv::Mat descLeftC_, descRightC_;                                /**< Stored current (C) descriptors for left and right images*/

    cv::Ptr<cv::FastFeatureDetector> fDetector_;                    /**< Feature detector (FAST)*/
    cv::Ptr<cv::xfeatures2d::BriefDescriptorExtractor> fExtractor_; /**< Feature decriptor extractor (BRIEF)*/

    RobustMatcher matcher_;                                         /**< Matcher*/

    std::vector<cv::DMatch> stereoMatchesC_;                        /**< Matching results for current(C) stereo pairs*/
    std::vector<cv::Point3f> pclC_;                                 /**< Stored current(C) 3D point cloud*/

    std::vector<double> SV_;                                        /* State vector for pose & map of 100 landmarks */
    Eigen::MatrixXd CM_;                                            /**< Covariance matrix*/
    Eigen::MatrixXd Q_;                                             /**< Motion Bias Covariance matrix*/
    Eigen::MatrixXd R_;                                             /**< Observation Bias Covariance matrix*/
    tf::Transform poseP_;                                           /**< Previous pose*/

    bool init_;                                                     /**< Flag indicating if EKF has been initialized*/
    bool pred_;                                                     /**< Flag indicating if prediction step has been done*/
    bool observedL_;                                                /**< Flag indicating if current landmark was observed before*/

    ros::NodeHandle nh_;                                            /**< ROS node handler*/
    ros::Subscriber kptSub_;                                        /**< VIO keyframe subscriber*/

    ros::Publisher posePub_;                                        /**< Publisher for predicted pose*/
    ros::Publisher pcPub_;                                          /**< Publisher for point cloud*/

    ros::Time timeP_;                                               /**< previous time*/

    std::vector<Landmark> lmrksC_;                                  /**< current landmarks*/
    std::vector<Landmark> map;                                      /**< map of landmarks*/

    cv::Mat KL_, PL_, RL_;                                          /**< Stereo camera parameters for left (L) camera*/
    cv::Mat KR_, PR_, RR_;                                          /**< Stereo camera parameters for right (R) camera*/

    int downsampling_;                                              /**< Downsampling factor for the input images*/
    int maxFeatures_;                                               /**< Maximum number of features to find in each bucket*/

    cv::Mat mapL1_, mapL2_, mapR1_, mapR2_;                         /**< Stereo rectification mappings*/

    int numLandmarks_;                                               /**< number of landmarks in the map*/

    double deltaT_;                                                 /**< time elapsed since last EKF iteration*/
    double simTh_;                                                  /**< similarity threshold for landmark global locations*/

    tf::Transform Tcam22imu0_, Timu02base_;                         /**< Auxiliary transforms between camera and base_link frames*/
};

#endif

ext: hpp