OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace / tests / query / StackUTest.cxxtest

223 lines · cxxtest ref:
/*
 * tests/query/StackUTest.cxxtest
 *
 * Copyright (C) 2009 Linas Vepstas <linasvepstas@gmail.com>
 * 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/atomspace/AtomSpace.h>
#include <opencog/query/InitiateSearchMixin.h>
#include <opencog/query/TermMatchMixin.h>
#include <opencog/query/SatisfyMixin.h>
#include <opencog/util/Logger.h>

#undef DEBUG

using namespace opencog;

class StackUTest :  public CxxTest::TestSuite
{
	private:
		AtomSpacePtr as;
		Handle svar, wvar;
		Handle p1, p2;
		Handle anch;

	public:

		StackUTest(void)
		{
			logger().set_level(Logger::DEBUG);
			logger().set_print_to_stdout_flag(true);
			logger().set_timestamp_flag(false);
		}

		~StackUTest()
		{
			// 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_stack(void);
};

class TestPMCB :
	public InitiateSearchMixin,
	public TermMatchMixin,
	public SatisfyMixin
{
	public:
	int lcnt;
	int scnt;
	Handle anch;
	TestPMCB(AtomSpace* as) :
		InitiateSearchMixin(as),
		TermMatchMixin(as), lcnt(0), scnt(0) {}

	virtual void set_pattern(const Variables& vars,
	                         const Pattern& pat)
	{
		InitiateSearchMixin::set_pattern(vars, pat);
		TermMatchMixin::set_pattern(vars, pat);
	}

	virtual bool clause_match(const Handle& pattrn,
	                          const Handle& grnd,
	                          const HandleMap& term_gnds)
	{
		// We need to call TermMatchMixin::clause_match
		// to handle the case of self-grounding, else the count
		// in the unit test is off by one.  (This is the
		// backwards-compat handling for the unit test.)
		bool OK = TermMatchMixin::clause_match(pattrn, grnd, term_gnds);
		if (not OK) return false;

		lcnt ++;
		// match the initial anchor clause, but reject the rest.
		return anch == grnd;
	}

	virtual bool propose_grounding(const HandleMap &var_soln,
	                               const HandleMap &pred_soln)
	{
		scnt ++;
		return false;
	}
};

/**
 * Run the matcher against the indicated variables and clauses,
 * using the indicated callback.
 */
static inline void match(PatternMatchCallback& pmcb,
                         const HandleSet &vars,
                         const HandleSeq &clauses)
{
	// Convert HandleSet vars to HandleSeq for VariableList
	HandleSeq varseq(vars.begin(), vars.end());
	Handle hvars = createLink(std::move(varseq), VARIABLE_LIST);

	// Convert HandleSeq clauses to body Handle
	Handle hbody;
	if (clauses.size() == 1) {
		hbody = clauses[0];
	} else {
		hbody = createLink(clauses, AND_LINK);
	}

	PatternLinkPtr slp(createPatternLink(hvars, hbody));
	pmcb.satisfy(slp);
}

/*
 * This function sets up the following structures:
 *
 * OrderedLink
 *     ConceptNode "# DOSTUFF"
 *     VariableNode "$sent"
 * ListLink
 *     VariableNode "$sent"
 *     VariableNode "$var"
 * 
 * The pattern matcher should be called seven times, once
 * for the ordered link, and 6 more times for the 6 list links.
 * One recent bug was triggered by the clause-matcher returning
 * "true", which resulted in it being invoked only half the time.
 */
#define an as->add_node
#define al as->add_link
void StackUTest::tearDown(void)
{
}
void StackUTest::setUp(void)
{
	as = createAtomSpace();

	// Create an implication link that will be tested.
	p1 = al(ORDERED_LINK,
		an(CONCEPT_NODE, "# DOSTUFF"),
		an(VARIABLE_NODE, "$sent")
	);
	p2 = al(LIST_LINK,
		svar = an(VARIABLE_NODE, "$sent"),
		wvar = an(VARIABLE_NODE, "$var")
	);

	// Create data on which the above pattern should match
	anch = al(ORDERED_LINK,
		an(CONCEPT_NODE, "# DOSTUFF"),
		an(CONCEPT_NODE, "my sentence")
	);
	al(LIST_LINK,
		an(CONCEPT_NODE, "my sentence"),
		an(CONCEPT_NODE, "thing A")
	);
	al(LIST_LINK,
		an(CONCEPT_NODE, "my sentence"),
		an(CONCEPT_NODE, "thing B")
	);
	al(LIST_LINK,
		an(CONCEPT_NODE, "my sentence"),
		an(CONCEPT_NODE, "thing C")
	);
	al(LIST_LINK,
		an(CONCEPT_NODE, "my sentence"),
		an(CONCEPT_NODE, "thing D")
	);
	al(LIST_LINK,
		an(CONCEPT_NODE, "my sentence"),
		an(CONCEPT_NODE, "thing E")
	);
	al(LIST_LINK,
		an(CONCEPT_NODE, "my sentence"),
		an(CONCEPT_NODE, "thing F")
	);
}

/*
 * stack-usage/bound-variable test.
 * Expect the user callback to be called for each match.
 * i.e. it should be called three times.
 *
 */
void StackUTest::test_stack(void)
{
	logger().debug("BEGIN TEST: %s", __FUNCTION__);

	HandleSet vars;
	vars.insert(svar);
	vars.insert(wvar);

	HandleSeq preds;
	preds.push_back(p1);
	preds.push_back(p2);

	TestPMCB pmcb(as.get());
	pmcb.anch = anch;
	match(pmcb, vars, preds);

	TSM_ASSERT_EQUALS("bad link-test call count", 7, pmcb.lcnt);
	TSM_ASSERT_EQUALS("bad solution call count", 0, pmcb.scnt);

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