OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace / tests / query / ScopeUTest.cxxtest

353 lines · cxxtest ref:
/*
 * tests/query/ScopeUTest.cxxtest
 *
 * Copyright (C) 2016 Linas Vepstas
 * All Rights Reserved
 *
 * 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 <opencog/guile/SchemeEval.h>
#include <opencog/atomspace/AtomSpace.h>
#include <opencog/util/Logger.h>

using namespace opencog;

#define an as->add_node
#define al as->add_link

class ScopeUTest: public CxxTest::TestSuite
{
private:
	AtomSpacePtr as;
	SchemeEval* eval;

public:
	ScopeUTest()
	{
		logger().set_level(Logger::DEBUG);
		logger().set_print_to_stdout_flag(true);

		as = createAtomSpace();
		eval = SchemeEval::get_scheme_evaluator(as);
		eval->eval("(add-to-load-path \"" PROJECT_SOURCE_DIR "\")");
	}

	~ScopeUTest()
	{
		// Erase the log file if no assertions failed.
		if (!CxxTest::TestTracker::tracker().suiteFailed())
			std::remove(logger().get_filename().c_str());
	}

	void setUp();
	void tearDown();

	void test_alpha();
	void test_alpha_reverse();
	void test_search();
	void test_search_reverse();
	void test_search_alt();
	void test_pushpop();
	void test_quote();
	void test_hide_1();
	void test_hide_2();
	void test_rule();
	void test_ill_query();
	void test_trivial_scope();
};

void ScopeUTest::tearDown()
{
	as->clear();
}

void ScopeUTest::setUp()
{
	as->clear();
	eval->eval("(load-from-path \"tests/query/test_types.scm\")");
}

/*
 * Test alpha conversion.
 */
void ScopeUTest::test_alpha()
{
	logger().debug("BEGIN TEST: %s", __FUNCTION__);

	eval->eval("(load-from-path \"tests/query/scope-search.scm\")");

	Handle c1 = eval->eval_h("(content-1)");
	Handle c2 = eval->eval_h("(content-2)");
	printf("c2 %s\n", c2->to_string().c_str());

	TS_ASSERT_EQUALS(c1, c2);
	logger().debug("END TEST: %s", __FUNCTION__);
}

/*
 * Test alpha conversion.
 */
void ScopeUTest::test_alpha_reverse()
{
	logger().debug("BEGIN TEST: %s", __FUNCTION__);

	eval->eval("(load-from-path \"tests/query/scope-search.scm\")");

	// Reverse the order of creation
	Handle c2 = eval->eval_h("(content-2)");
	Handle c1 = eval->eval_h("(content-1)");
	printf("c1 %s\n", c1->to_string().c_str());

	TS_ASSERT_EQUALS(c1, c2);
	logger().debug("END TEST: %s", __FUNCTION__);
}

/*
 * Test search of embedded scope links
 */
void ScopeUTest::test_search()
{
	logger().debug("BEGIN TEST: %s", __FUNCTION__);

	eval->eval("(load-from-path \"tests/query/scope-search.scm\")");

	Handle c1 = eval->eval_h("(content-1)");
	Handle r1 = eval->eval_h("(cog-execute! (member-to-evaluation-2-1-rule))");

	Handle c2 = eval->eval_h("(content-2)");
	Handle r2 = eval->eval_h("(cog-execute! (member-to-evaluation-2-1-rule))");

	TS_ASSERT_EQUALS(c1, c2);
	TS_ASSERT_EQUALS(r1, r2);
	logger().debug("END TEST: %s", __FUNCTION__);
}

/*
 * Test search of embedded scope links.
 * Same as above, but reversed order.
 */
void ScopeUTest::test_search_reverse()
{
	logger().debug("BEGIN TEST: %s", __FUNCTION__);

	eval->eval("(load-from-path \"tests/query/scope-search.scm\")");

	Handle c2 = eval->eval_h("(content-2)");
	Handle r2 = eval->eval_h("(cog-execute! (member-to-evaluation-2-1-rule))");

	Handle c1 = eval->eval_h("(content-1)");
	Handle r1 = eval->eval_h("(cog-execute! (member-to-evaluation-2-1-rule))");

	TS_ASSERT_EQUALS(c1, c2);
	TS_ASSERT_EQUALS(r1, r2);
	logger().debug("END TEST: %s", __FUNCTION__);
}

/*
 * Test search of embedded scope links, with renamed vars
 */
void ScopeUTest::test_search_alt()
{
	logger().debug("BEGIN TEST: %s", __FUNCTION__);

	eval->eval("(load-from-path \"tests/query/scope-search.scm\")");

	Handle c1 = eval->eval_h("(content-1)");
	Handle r1 = eval->eval_h("(cog-execute! (member-to-evaluation-2-1-alt))");

	Handle c2 = eval->eval_h("(content-2)");
	Handle r2 = eval->eval_h("(cog-execute! (member-to-evaluation-2-1-rule))");

	TS_ASSERT_EQUALS(c1, c2);
	TS_ASSERT_EQUALS(r1, r2);
	logger().debug("END TEST: %s", __FUNCTION__);
}

/*
 * Test search of embedded scope links.
 * This time, the dataset includes data that fails to
 * match, and thus might corrupt the search stack.
 */
void ScopeUTest::test_pushpop()
{
	logger().debug("BEGIN TEST: %s", __FUNCTION__);

	eval->eval("(load-from-path \"tests/query/scope-search.scm\")");
	eval->eval("(load-from-path \"tests/query/scope-confound.scm\")");

	Handle c1 = eval->eval_h("(content-1)");
	eval->eval_h("(evaluation-to-member-2-rule-loose)");
	eval->eval_h("(evaluation-to-member-2-rule)");
	eval->eval_h("(cog-execute! (member-to-evaluation-2-1-rule))");

	Handle em = eval->eval_h("(cog-execute! (evaluation-to-member-2-rule))");
	TS_ASSERT_EQUALS(1, em->get_arity());
	Handle c2 = em->getOutgoingAtom(0);

	TS_ASSERT_EQUALS(c1, c2);
	logger().debug("END TEST: %s", __FUNCTION__);
}

/*
 * Test search of embedded scope links.
 * The search pattern includes QuoteLinks that have to be navigated
 * correctly, in order for things to work.
 */
void ScopeUTest::test_quote()
{
	logger().debug("BEGIN TEST: %s", __FUNCTION__);

	eval->eval("(load-from-path \"tests/query/scope-quote.scm\")");

	Handle list = eval->eval_h("(cog-execute! blist)");
	Handle andl = eval->eval_h("(cog-execute! bland)");

	Handle elist = eval->eval_h("(SetLink (List p-lamb q-lamb))");
	Handle eandl = eval->eval_h(
		"(SetLink (List p-lamb q-lamb) (List q-lamb p-lamb))");

	logger().debug("Expect: %s", elist->to_string().c_str());
	logger().debug("List got: %s", list->to_string().c_str());

	logger().debug("AndExpect: %s", eandl->to_string().c_str());
	logger().debug("AndLink got: %s", andl->to_string().c_str());

	TS_ASSERT_EQUALS(list, elist);
	TS_ASSERT_EQUALS(andl, eandl);
	logger().debug("END TEST: %s", __FUNCTION__);
}

/*
 * Test the hiding of bound variables.
 */
void ScopeUTest::test_hide_1()
{
	logger().debug("BEGIN TEST: %s", __FUNCTION__);

	eval->eval("(load-from-path \"tests/query/scope-hide.scm\")");

	// Alpha conversion should make for equal.
	Handle getx = eval->eval_h("getx");
	Handle getv = eval->eval_h("getv");
	TS_ASSERT_EQUALS(getx, getv);

	Handle forall = eval->eval_h("forall");
	Handle varx = forall->getOutgoingAtom(0);

	Handle forpat = getx->getOutgoingAtom(0)->getOutgoingAtom(0)->getOutgoingAtom(0);
	Handle patvar = forpat->getOutgoingAtom(0);

	printf("varx = %s\n", varx->to_string().c_str());
	printf("patvar = %s\n", patvar->to_string().c_str());
	// This unit test doesn't test anything critical if the pattern
	// and the ground don't happen to have the same variable.
	TS_ASSERT_EQUALS(varx, patvar);

	Handle result = eval->eval_h("(cog-execute! getx)");
	TS_ASSERT_EQUALS(1, result->get_arity());

	Handle expected = eval->eval_h(
		"(SetLink (List"
				"(Variable \"$X\")"
				"(Edge (Predicate \"P\") (Variable \"$X\"))))"
	);

	TS_ASSERT_EQUALS(result, expected);

	logger().debug("END TEST: %s", __FUNCTION__);
}

void ScopeUTest::test_hide_2()
{
	logger().debug("BEGIN TEST: %s", __FUNCTION__);

	std::string eval_result = eval->eval("(load-from-path \"tests/query/scope-hide.scm\")");
	Handle rule = eval->eval_h("rule");
	Handle result = HandleCast(rule->execute(as.get()));
	Handle expected = al(SET_LINK, rule->getOutgoingAtom(0)->getOutgoingAtom(2));

	logger().debug() << "result = " << oc_to_string(result);
	logger().debug() << "expected = " << oc_to_string(expected);

	TS_ASSERT_EQUALS(result, expected);

	logger().debug("END TEST: %s", __FUNCTION__);
}

void ScopeUTest::test_rule()
{
	logger().debug("BEGIN TEST: %s", __FUNCTION__);

	Handle ground = eval->eval_h(
		"(RuleLink"
		"  (TypedVariableLink"
		"    (VariableNode \"$A\")"
		"    (TypeNode \"MeetLink\"))"
		"  (DontExecLink (DefinedSchemaNode \"deduction-inheritance-rule\"))"
		"  (DontExecLink (VariableNode \"$A\")))"
		);
	Handle query = eval->eval_h(
		"(CollectionOf (Meet"
		"  (TypedVariableLink"
		"    (VariableNode \"$Rule\")"
		"    (TypeNode \"DefinedSchemaNode\"))"
		"  (RuleLink"
		"    (TypedVariableLink"
		"      (VariableNode \"$A\")"
		"      (TypeNode \"MeetLink\"))"
		"    (DontExecLink (VariableNode \"$Rule\"))"
		"    (DontExecLink (VariableNode \"$A\")))))"
		);

	Handle result = HandleCast(query->execute(as.get()));
	Handle expected =
		al(SET_LINK,
		   eval->eval_h("(DefinedSchemaNode \"deduction-inheritance-rule\")"));

	logger().debug() << "result = " << oc_to_string(result);
	logger().debug() << "expected = " << oc_to_string(expected);

	TS_ASSERT_EQUALS(result, expected);

	logger().debug("END TEST: %s", __FUNCTION__);
}

void ScopeUTest::test_ill_query()
{
	logger().debug("BEGIN TEST: %s", __FUNCTION__);

	TS_ASSERT_THROWS_ANYTHING(eval->eval_v("(Query (Variable \"$X\"))"));

	logger().debug("END TEST: %s", __FUNCTION__);
}

void ScopeUTest::test_trivial_scope()
{
	logger().debug("BEGIN TEST: %s", __FUNCTION__);

	Handle sc = al(SCOPE_LINK, an(VARIABLE_NODE, "$X"));

	logger().debug() << "sc = " << oc_to_string(sc);

	TS_ASSERT(sc);

	logger().debug("END TEST: %s", __FUNCTION__);
}

#undef an
#undef al