OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace / tests / query / SequenceUTest.cxxtest

340 lines · cxxtest ref:
/*
 * tests/query/SequenceUTest.cxxtest
 *
 * Copyright (C) 2015 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/atoms/grant/StateLink.h>
#include <opencog/util/Logger.h>
#include <opencog/atoms/value/BoolValue.h>

using namespace opencog;

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

public:
	SequenceUTest(void)
	{
		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 "\")");
	}

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

	void setUp(void);
	void tearDown(void);

	void test_sequence(void);
	void test_presence(void);
	void test_fallback(void);
	void test_pass_presence(void);
	void test_trivial(void);
	void test_or_put(void);
};

void SequenceUTest::tearDown(void)
{
}

void SequenceUTest::setUp(void)
{
}

/*
 * Basic SequenceLink unit test.
 */
void SequenceUTest::test_sequence(void)
{
	logger().debug("BEGIN TEST: %s", __FUNCTION__);

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

	// This should throw an exception from scheme.
	// Disable logging so as to avoid the hopeless spew of errors.
	logger().set_level(Logger::NONE);
	bool caught_exception = false;
	try
	{
		eval->eval_v("(cog-execute! (off-road))");
	}
	catch (const RuntimeException &e)
	{
		caught_exception = true;
	}
	TS_ASSERT_EQUALS(true, caught_exception);
	logger().set_level(Logger::DEBUG);

	// This should not throw any exceptions.
	ValuePtr vp = eval->eval_v("(cog-execute! (traffic-lights))");
	BoolValuePtr bvp = BoolValueCast(vp);
	TS_ASSERT(not bvp->get_bit(0));

	// Check the number of green lights and red lights that were
	// evaluated. The pattern has two green lights and one red
	// light... The GPN's count how many times these are hit, and
	// the counts should be correct.
	std::string sngreen = eval->eval("num-green");
	std::string snred = eval->eval("num-red");
	int ngreen = atoi(sngreen.c_str());
	int nred = atoi(snred.c_str());
	TS_ASSERT_EQUALS(2, ngreen);
	TS_ASSERT_EQUALS(1, nred);

	// ----------------------------------------------------------
	// There are two green lights here; the BoolValue should be "true".
	vp = eval->eval_v("(cog-execute! (all-green))");
	bvp = BoolValueCast(vp);
	TS_ASSERT(bvp->get_bit(0));

	// Should have seen two more green lights, and no reds.
	sngreen = eval->eval("num-green");
	snred = eval->eval("num-red");
	ngreen = atoi(sngreen.c_str());
	nred = atoi(snred.c_str());
	TS_ASSERT_EQUALS(4, ngreen);
	TS_ASSERT_EQUALS(1, nred);

	// ----------------------------------------------------------
	// There are four green lights here; the third one is wrapped
	// by a NotLink.  That should halt the pregression, and return
	// false.
	vp = eval->eval_v("(cog-execute! (anti-green))");
	bvp = BoolValueCast(vp);
	TS_ASSERT(not bvp->get_bit(0));

	// Should have seen three more green lights, and no reds.
	sngreen = eval->eval("num-green");
	snred = eval->eval("num-red");
	ngreen = atoi(sngreen.c_str());
	nred = atoi(snred.c_str());
	TS_ASSERT_EQUALS(7, ngreen);
	TS_ASSERT_EQUALS(1, nred);

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

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

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

	ValuePtr vp = eval->eval_v("(cog-execute! empty-sequence)");
	BoolValuePtr bvp = BoolValueCast(vp);
	TS_ASSERT(bvp->get_bit(0));

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

/*
 * SequentialOrLink unit test.
 */
void SequenceUTest::test_fallback(void)
{
	logger().debug("BEGIN TEST: %s", __FUNCTION__);

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

	// This should not throw any exceptions.
	ValuePtr vp = eval->eval_v("(cog-execute! drag-race)");
	BoolValuePtr bvp = BoolValueCast(vp);
	TS_ASSERT(bvp->get_bit(0));

	// Check the number of green lights and red lights that were
	// evaluated. The pattern has two green lights and one red
	// light... The GPN's count how many times these are hit, and
	// the counts should be correct.
	std::string sngreen = eval->eval("num-green");
	std::string snred = eval->eval("num-red");
	int ngreen = atoi(sngreen.c_str());
	int nred = atoi(snred.c_str());
	TS_ASSERT_EQUALS(1, ngreen);
	TS_ASSERT_EQUALS(3, nred);

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

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

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

	ValuePtr vp = eval->eval_v("(cog-execute! or-presence)");
	BoolValuePtr bvp = BoolValueCast(vp);
	TS_ASSERT(bvp->get_bit(0));

	std::string sntrig = eval->eval("trig");
	int ntrig = atoi(sntrig.c_str());
	TS_ASSERT_EQUALS(1, ntrig);

	// -------------------------------------------------------
	vp = eval->eval_v("(cog-execute! and-absence)");
	bvp = BoolValueCast(vp);
	TS_ASSERT(bvp->get_bit(0));

	sntrig = eval->eval("trig");
	ntrig = atoi(sntrig.c_str());
	TS_ASSERT_EQUALS(2, ntrig);

	// -------------------------------------------------------
	vp = eval->eval_v("(cog-execute! and-not-present)");
	bvp = BoolValueCast(vp);
	TS_ASSERT(bvp->get_bit(0));

	sntrig = eval->eval("trig");
	ntrig = atoi(sntrig.c_str());
	TS_ASSERT_EQUALS(3, ntrig);

	// -------------------------------------------------------
	vp = eval->eval_v("(cog-execute! or-not-absent)");
	bvp = BoolValueCast(vp);
	TS_ASSERT(bvp->get_bit(0));

	sntrig = eval->eval("trig");
	ntrig = atoi(sntrig.c_str());
	TS_ASSERT_EQUALS(4, ntrig);

	// -------------------------------------------------------
	// and now, all the above should fail...
	eval->eval_h("(EvaluationLink (PredicateNode \"visible\")"
				 "(ListLink (ConceptNode \"yes it is\")))");

	int no_change = 4;

	// ----
	vp = eval->eval_v("(cog-execute! or-presence)");
	bvp = BoolValueCast(vp);
	TS_ASSERT(bvp->get_bit(0)); // return true

	ntrig = atoi(eval->eval("trig").c_str());
	TS_ASSERT_EQUALS(no_change, ntrig);

	// ----
	vp = eval->eval_v("(cog-execute! and-absence)");
	bvp = BoolValueCast(vp);
	TS_ASSERT(not bvp->get_bit(0));  // return false

	ntrig = atoi(eval->eval("trig").c_str());
	TS_ASSERT_EQUALS(no_change, ntrig);

#if BROKEN_FIXME_XXX
// The below is a valid test, and it should pass; however, the
// pattern matcher fails on these two. Since I'm too lazy to fix,
// just right now, I'm just gonna stub these out instead.
	// ----
	vp = eval->eval_v("(cog-execute! and-not-present)");
	bvp = BoolValueCast(vp);
	TS_ASSERT(not bvp->get_bit(0)); // return false

	ntrig = atoi(eval->eval("trig").c_str());
	TS_ASSERT_EQUALS(no_change, ntrig);

	// ----
	vp = eval->eval_v("(cog-execute! or-not-absent)");
	bvp = BoolValueCast(vp);
	TS_ASSERT(bvp->get_bit(0)); // return true

	ntrig = atoi(eval->eval("trig").c_str());
	TS_ASSERT_EQUALS(no_change, ntrig);
#endif

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

/*
 * Trivial SequenceLink unit test.
 * Actually, this is testing QueryLink, as a surrogate for a trivial
 * SequenceLink. This trivial case was failing yesterday... :-/
 */
void SequenceUTest::test_trivial(void)
{
	logger().debug("BEGIN TEST: %s", __FUNCTION__);

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

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

	result = eval->eval_h("(cog-execute! (CollectionOf get-nothing))");
	TS_ASSERT_EQUALS(result->get_arity(), 0);

	result = eval->eval_h("(cog-execute! (CollectionOf get-not-true))");
	TS_ASSERT_EQUALS(result->get_arity(), 0);

	result = eval->eval_h("(cog-execute! (CollectionOf get-not-false))");
	TS_ASSERT_EQUALS(result->get_arity(), 1);

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

// This is an interesting test case .. the PutLink writes atoms to the
// main atomspace, instead of a temporary scratch space!
void SequenceUTest::test_or_put(void)
{
	logger().debug("BEGIN TEST: %s", __FUNCTION__);

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

	// ----
	// Insert eval link to prevent it from tripping.
	eval->eval_h(
		"(EvaluationLink (PredicateNode \"yes-visible\")"
		"	(ListLink (ConceptNode \"is-vis\")))");
	ValuePtr vp = eval->eval_v("(cog-execute! or-visible-put)");
	BoolValuePtr bvp = BoolValueCast(vp);
	TS_ASSERT(bvp->get_bit(0));

	// Check the state. Should have a list of length 1,
	// with a variable only; that is, the state was NOT set.
	Handle iset = eval->eval_h(
		  "(cdr (cog-incoming-set (AnchorNode \"state\")))");
	TS_ASSERT_EQUALS(iset, Handle::UNDEFINED);

	// ----
	vp = eval->eval_v("(cog-execute! or-put)");
	bvp = BoolValueCast(vp);
	TS_ASSERT(bvp->get_bit(0));

	// Check the state.
	Handle not_viz = eval->eval_h("(ConceptNode \"not-vis\")");
	Handle stateh = eval->eval_h("(AnchorNode \"state\")");
	Handle state = StateLink::get_state(stateh);
	TS_ASSERT_EQUALS(state, not_viz);

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