OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace-storage / tests / persist / sexpr / CommandsUTest.cxxtest

486 lines · cxxtest ref:
/*
 * 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__);
}