/*
* CommandsUTest.cxxtest
*
* Copyright (c) 2020 Linas Vepstas
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License v3 as
* published by the Free Software Foundation and including the exceptions
* at http://opencog.org/wiki/Licenses
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, write to:
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <iomanip>
#include <opencog/util/Logger.h>
#include <opencog/atomspace/AtomSpace.h>
#include <opencog/atoms/value/FloatValue.h>
#include <opencog/atoms/value/StringValue.h>
#include "opencog/persist/sexcom/Dispatcher.h"
using namespace opencog;
class CommandsUTest : public CxxTest::TestSuite
{
private:
AtomSpacePtr as;
public:
CommandsUTest()
{
logger().set_print_to_stdout_flag(true);
as = createAtomSpace();
}
void setUp() { as->clear(); }
void tearDown() {}
void test_node();
void test_node_quoted();
void test_link();
void test_empty_link();
void test_set_value();
void test_set_value_quoted();
void test_set_several_quoted();
void test_get_value();
void test_get_value_quoted();
void test_set_values();
void test_get_values();
void test_extract();
void test_execute();
};
// Test cog-node
void CommandsUTest::test_node()
{
logger().info("BEGIN TEST: %s", __FUNCTION__);
std::string in = R"((cog-node 'Concept "foo"))";
Dispatcher com;
com.set_base_space(as);
std::string out = com.interpret_command(in);
printf("Got %s\n", out.c_str());
printf("AtomSpace is %s\n", as->to_string().c_str());
TS_ASSERT_EQUALS(0, as->get_size());
TS_ASSERT(0 == out.compare("#f\n"));
as->add_node(CONCEPT_NODE, "foo");
out = com.interpret_command(in);
printf("Got >>%s<<\n", out.c_str());
TS_ASSERT_EQUALS(1, as->get_size());
TS_ASSERT(0 == out.compare("(ConceptNode \"foo\")"));
logger().info("END TEST: %s", __FUNCTION__);
}
// Test cog-node with quoted names
void CommandsUTest::test_node_quoted()
{
logger().info("BEGIN TEST: %s", __FUNCTION__);
std::stringstream ss;
ss << "(cog-node 'Concept " << std::quoted(R"("f""o"""o"!!!"")") << ")";
std::string in = ss.str();
printf("Input %s\n", in.c_str());
Dispatcher com;
com.set_base_space(as);
std::string out = com.interpret_command(in);
printf("Got %s\n", out.c_str());
TS_ASSERT_EQUALS(0, as->get_size());
TS_ASSERT(0 == out.compare("#f\n"));
Handle h = as->add_node(CONCEPT_NODE, R"("f""o"""o"!!!"")");
printf("Added atom %s\n", h->to_short_string().c_str());
out = com.interpret_command(in);
printf("Got >>%s<<\n", out.c_str());
TS_ASSERT_EQUALS(1, as->get_size());
std::stringstream sso;
sso << "(ConceptNode " << std::quoted(R"("f""o"""o"!!!"")") << ")";
std::string expect = sso.str();
printf("Expect >>%s<<\n", expect.c_str());
TS_ASSERT(0 == out.compare(expect));
logger().info("END TEST: %s", __FUNCTION__);
}
// Test cog-link
void CommandsUTest::test_link()
{
logger().info("BEGIN TEST: %s", __FUNCTION__);
Handle ca = as->add_node(CONCEPT_NODE, "a");
Handle cb = as->add_node(CONCEPT_NODE, "b");
std::string in = R"((cog-link 'List (Concept "a") (Concept "b")))";
Dispatcher com;
com.set_base_space(as);
std::string out = com.interpret_command(in);
printf("Got %s\n", out.c_str());
TS_ASSERT_EQUALS(2, as->get_size());
TS_ASSERT(0 == out.compare("#f\n"));
as->add_link(LIST_LINK, ca, cb);
out = com.interpret_command(in);
printf("Got >>%s<<\n", out.c_str());
TS_ASSERT_EQUALS(3, as->get_size());
TS_ASSERT(0 == out.compare(
"(ListLink (ConceptNode \"a\")(ConceptNode \"b\"))"));
logger().info("END TEST: %s", __FUNCTION__);
}
// Test empty ListLink parsing (bug fix for whitespace handling)
void CommandsUTest::test_empty_link()
{
logger().info("BEGIN TEST: %s", __FUNCTION__);
// Test that empty ListLink without whitespace parses correctly
std::string in = R"((cog-execute-cache! (ListLink) (Predicate "test-key")))";
Dispatcher com;
com.set_base_space(as);
std::string out = com.interpret_command(in);
printf("Got %s\n", out.c_str());
// ListLink is not executable, so returns #f, but parsing should succeed
TS_ASSERT(0 == out.compare("#f"));
// Verify atoms were created (ListLink + Predicate)
TS_ASSERT(2 == as->get_size());
// Test with explicit whitespace (should also work)
as->clear();
in = R"((cog-execute-cache! (ListLink ) (Predicate "test-key2")))";
out = com.interpret_command(in);
printf("Got %s\n", out.c_str());
TS_ASSERT(0 == out.compare("#f"));
TS_ASSERT(2 == as->get_size());
logger().info("END TEST: %s", __FUNCTION__);
}
// Test cog-set-value!
void CommandsUTest::test_set_value()
{
logger().info("BEGIN TEST: %s", __FUNCTION__);
std::string in =
R"((cog-set-value! (Concept "a") (Predicate "key") (FloatValue 0.6 0.8)))";
Dispatcher com;
com.set_base_space(as);
std::string out = com.interpret_command(in);
printf("Got %s\n", out.c_str());
TS_ASSERT_EQUALS(3, as->get_size());
TS_ASSERT(0 == out.compare(""));
Handle h = as->get_node(CONCEPT_NODE, "a");
TS_ASSERT(nullptr != h);
Handle key = as->add_node(PREDICATE_NODE, "key");
ValuePtr vp = h->getValue(key);
TS_ASSERT(nullptr != vp);
TS_ASSERT_EQUALS(FLOAT_VALUE, vp->get_type());
logger().info("END TEST: %s", __FUNCTION__);
}
// Test cog-set-value! with nested quotes
void CommandsUTest::test_set_value_quoted()
{
logger().info("BEGIN TEST: %s", __FUNCTION__);
std::stringstream ss;
ss << "(cog-set-value! (Concept \"a\") (Predicate \"key\") ";
ss << "(StringValue ";
ss << std::quoted(R"("f""o"""o"!!!"")") << " ";
ss << std::quoted(R"(b"""a"""r)") << "))";
std::string in = ss.str();
printf("Input %s\n", in.c_str());
Dispatcher com;
com.set_base_space(as);
std::string out = com.interpret_command(in);
printf("Got %s\n", out.c_str());
TS_ASSERT_EQUALS(3, as->get_size());
TS_ASSERT(0 == out.compare(""));
Handle h = as->get_node(CONCEPT_NODE, "a");
TS_ASSERT(nullptr != h);
Handle key = as->add_node(PREDICATE_NODE, "key");
ValuePtr vp = h->getValue(key);
TS_ASSERT(nullptr != vp);
TS_ASSERT_EQUALS(STRING_VALUE, vp->get_type());
std::string vps = vp->to_string();
printf("Value %s\n", vps.c_str());
std::stringstream oss;
oss << "(StringValue ";
oss << std::quoted(R"("f""o"""o"!!!"")") << " ";
oss << std::quoted(R"(b"""a"""r)") << ")";
std::string xpt = oss.str();
printf("Expect %s\n", xpt.c_str());
TS_ASSERT(0 == vps.compare(xpt));
logger().info("END TEST: %s", __FUNCTION__);
}
// Test cog-set-values! with a complicted expression
void CommandsUTest::test_set_several_quoted()
{
logger().info("BEGIN TEST: %s", __FUNCTION__);
std::string in =
R"((cog-set-values! (ListLink (ConceptNode "x \"float\" node")(ConceptNode "b float node"))(alist (cons (PredicateNode "some (\"pred\"))) key")(FloatValue 1.109876543210988 2.123456789012345e+37 3.210987654321099e-250))(cons (PredicateNode "*-TruthValueKey-*")(FloatValue 0.11 100)))))";
printf("Input %s\n", in.c_str());
Dispatcher com;
com.set_base_space(as);
std::string out = com.interpret_command(in);
printf("Got %s\n", out.c_str());
printf("AtomSpace contents: %s\n", as->to_string().c_str());
TS_ASSERT_EQUALS(6, as->get_size());
TS_ASSERT(0 == out.compare(""));
Handle hx = as->get_node(CONCEPT_NODE, "x \"float\" node");
TS_ASSERT(nullptr != hx);
Handle hb = as->get_node(CONCEPT_NODE, "b float node");
TS_ASSERT(nullptr != hb);
Handle hl = as->get_link(LIST_LINK, {hx, hb});
TS_ASSERT(nullptr != hl);
Handle key = as->get_node(PREDICATE_NODE, "some (\"pred\"))) key");
TS_ASSERT(nullptr != key);
ValuePtr vp = hl->getValue(key);
TS_ASSERT(nullptr != vp);
TS_ASSERT_EQUALS(FLOAT_VALUE, vp->get_type());
logger().info("END TEST: %s", __FUNCTION__);
}
// Test cog-value
void CommandsUTest::test_get_value()
{
logger().info("BEGIN TEST: %s", __FUNCTION__);
Handle h = as->add_node(CONCEPT_NODE, "a");
Handle key = as->add_node(PREDICATE_NODE, "key");
Handle fiz = as->add_node(PREDICATE_NODE, "fiz");
Handle buz = as->add_node(PREDICATE_NODE, "buz");
h->setValue(key, createFloatValue(std::vector<double>{4, 5, 6}));
h->setValue(fiz, createStringValue(std::vector<std::string>{"a", "b"}));
h->setValue(buz, ValueCast(createFloatValue(std::vector<double>{0.3, 0.4})));
std::string in = "(cog-value (Concept \"a\") (Predicate \"key\"))";
Dispatcher com;
com.set_base_space(as);
std::string out = com.interpret_command(in);
printf("Got %s\n", out.c_str());
TS_ASSERT(0 == out.compare(0, 12, "(FloatValue "));
in = "(cog-value (Concept \"a\") (Predicate \"fiz\"))";
out = com.interpret_command(in);
printf("Got %s\n", out.c_str());
TS_ASSERT(0 == out.compare("(StringValue \"a\" \"b\")"));
in = "(cog-value (Concept \"a\") (Predicate \"buz\"))";
out = com.interpret_command(in);
printf("Got %s\n", out.c_str());
TS_ASSERT(0 == out.compare(0, 12, "(FloatValue "));
logger().info("END TEST: %s", __FUNCTION__);
}
// Test cog-value, with quoted string
void CommandsUTest::test_get_value_quoted()
{
logger().info("BEGIN TEST: %s", __FUNCTION__);
Handle h = as->add_node(CONCEPT_NODE, "a");
Handle key = as->add_node(PREDICATE_NODE, "key");
Handle fiz = as->add_node(PREDICATE_NODE, "fiz");
Handle buz = as->add_node(PREDICATE_NODE, "buz");
h->setValue(key, createFloatValue(std::vector<double>{4, 5, 6}));
h->setValue(fiz, createStringValue(std::vector<std::string>
{"\"f\"\"o\"\"\"o\"!!!\"\"", "b\"\"\"a\"\"\"r"}));
h->setValue(buz, ValueCast(createFloatValue(std::vector<double>{0.3, 0.4})));
std::string in = "(cog-value (Concept \"a\") (Predicate \"fiz\"))";
Dispatcher com;
com.set_base_space(as);
std::string out = com.interpret_command(in);
printf("Got %s\n", out.c_str());
std::stringstream oss;
oss << "(StringValue ";
oss << std::quoted(R"("f""o"""o"!!!"")") << " ";
oss << std::quoted(R"(b"""a"""r)") << ")";
std::string xpt = oss.str();
printf("Expect %s\n", xpt.c_str());
TS_ASSERT(0 == out.compare(xpt));
logger().info("END TEST: %s", __FUNCTION__);
}
// Test cog-set-values!
void CommandsUTest::test_set_values()
{
logger().info("BEGIN TEST: %s", __FUNCTION__);
std::string in = "(cog-set-values! (Concept \"a\") (alist "
"(cons (Predicate \"fiz\") (FloatValue 1 2 3))"
"(cons (Predicate \"buz\") (StringValue \"gee\" \"golly\"))))";
Dispatcher com;
com.set_base_space(as);
std::string out = com.interpret_command(in);
printf("Got %s\n", out.c_str());
TS_ASSERT(0 == out.compare(""));
TS_ASSERT_EQUALS(4, as->get_size());
Handle h = as->get_node(CONCEPT_NODE, "a");
TS_ASSERT(nullptr != h);
Handle key = as->add_node(PREDICATE_NODE, "fiz");
ValuePtr vp = h->getValue(key);
TS_ASSERT(nullptr != vp);
TS_ASSERT_EQUALS(FLOAT_VALUE, vp->get_type());
key = as->add_node(PREDICATE_NODE, "buz");
vp = h->getValue(key);
TS_ASSERT(nullptr != vp);
TS_ASSERT_EQUALS(STRING_VALUE, vp->get_type());
logger().info("END TEST: %s", __FUNCTION__);
}
// Test cog-keys->alist
void CommandsUTest::test_get_values()
{
logger().info("BEGIN TEST: %s", __FUNCTION__);
Handle h = as->add_node(CONCEPT_NODE, "a");
Handle key = as->add_node(PREDICATE_NODE, "key");
Handle fiz = as->add_node(PREDICATE_NODE, "fiz");
Handle buz = as->add_node(PREDICATE_NODE, "buz");
h->setValue(key, createFloatValue(std::vector<double>{4, 5, 6}));
h->setValue(fiz, createStringValue(std::vector<std::string>{"a", "b"}));
h->setValue(buz, ValueCast(createFloatValue(std::vector<double>{3, 0.4, 42})));
h->setValue(truth_key(), createFloatValue(std::vector<double>{0.1, 0.9}));
std::string in = "(cog-keys->alist (Concept \"a\"))";
Dispatcher com;
com.set_base_space(as);
std::string out = com.interpret_command(in);
printf("Got %zu %s\n", out.size(), out.c_str());
TS_ASSERT(0 == out.compare(0, 17, "(((PredicateNode "));
TS_ASSERT(std::string::npos != out.find("FloatValue"));
TS_ASSERT(200 <= out.size());
logger().info("END TEST: %s", __FUNCTION__);
}
// Test cog-extract!
void CommandsUTest::test_extract()
{
logger().info("BEGIN TEST: %s", __FUNCTION__);
Handle a = as->add_node(CONCEPT_NODE, "a");
Handle l = as->add_link(LIST_LINK, a);
std::string in = "(cog-extract! (Concept \"a\"))";
Dispatcher com;
com.set_base_space(as);
std::string out = com.interpret_command(in);
printf("Got %s\n", out.c_str());
TS_ASSERT(0 == out.compare("#f"));
Handle ax = as->get_node(CONCEPT_NODE, "a");
TS_ASSERT(a == ax);
in = "(cog-extract-recursive! (Concept \"a\"))";
out = com.interpret_command(in);
printf("Got %s\n", out.c_str());
TS_ASSERT(0 == out.compare("#t"));
ax = as->get_node(CONCEPT_NODE, "a");
TS_ASSERT(nullptr == ax);
logger().info("END TEST: %s", __FUNCTION__);
}
// Test cog-execute-cache!
void CommandsUTest::test_execute()
{
logger().info("BEGIN TEST: %s", __FUNCTION__);
// Populate the AtomSpace
Handle a = as->add_node(CONCEPT_NODE, "a");
Handle b = as->add_node(CONCEPT_NODE, "b");
Handle l = as->add_link(LIST_LINK, a, b);
// Run this query
std::string in =
"(cog-execute-cache! "
"(Meet (Variable \"x\")"
"(Present (List (Variable \"x\") (Concept \"b\"))))"
"(Predicate \"key\"))";
Dispatcher com;
com.set_base_space(as);
std::string out = com.interpret_command(in);
printf("Got >>%s<<\n", out.c_str());
// TS_ASSERT(0 == out.compare("(QueueValue (Concept \"a\"))"));
TS_ASSERT(0 == out.compare("(UnisetValue (Concept \"a\"))"));
// Verify that the result was placed onto the key.
#define an as->add_node
#define al as->add_link
Handle q = al(MEET_LINK, an(VARIABLE_NODE, "x"),
al(PRESENT_LINK,
al(LIST_LINK, an(VARIABLE_NODE, "x"), an(CONCEPT_NODE, "b"))));
Handle key = as->add_node(PREDICATE_NODE, "key");
ValuePtr vp = q->getValue(key);
TS_ASSERT(nullptr != vp);
printf("Got %s\n", vp->to_short_string().c_str());
TS_ASSERT(vp->is_type(CONTAINER_VALUE));
logger().info("END TEST: %s", __FUNCTION__);
}