OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace / tests / query / CacheHitUTest.cxxtest

157 lines · cxxtest ref:
/*
 * tests/query/CacheHitUTest.cxxtest
 *
 * Copyright (C) 2014, 2015, 2021 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;

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

public:
	CacheHitUTest(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 "\")");

	}

	~CacheHitUTest()
	{
		// 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 add_cruft(char);
	void test_hit(void);
};

void CacheHitUTest::tearDown(void)
{
}

void CacheHitUTest::setUp(void)
{
}

void CacheHitUTest::add_cruft(char junk)
{
	char buf[8192];
	snprintf(buf, 8192,
"  (EvaluationLink"
"    (PredicateNode \"*-word-shape pair-*\")"
"    (ConceptNode \"d\")"
"    (EvaluationLink"
"      (PredicateNode \"*-shape-*\")"
"      (ConceptNode \"%c\")"
"      (Connector (ConceptNode \"e\") (Sex \"-\"))"
"      (Connector (ConceptNode \"j\") (Sex \"+\"))"
"      (Connector (ConceptNode \"g\") (Sex \"+\"))"
"      (Connector (VariableNode \"$connector-word\") (Sex \"+\"))"
"      (Connector (ConceptNode \"!\") (Sex \"+\"))"
"    ))", junk);
	eval->eval(buf);
}

/*
 * Verify that a cache hit does not result in a crash.
 *
 * This bug can be hard to trigger, as it depends on the order
 * in which the search is performed, and the search order depends
 * on how the atoms lay out in RAM. So the actual test strategy
 * is to loop: in each loop, clear the atomspace, add some cruft
 * to scramble the memory order, and then run the test. This
 * eventually results in a core dump, when the patch in pull req
 * opencog/atomspace#2803 is not applied.
 *
 * There are three places where the search can start, and so,
 * if memory really is randomized, it should not take more than
 * about 6-8 iterations of the loop before the bug is hit.
 */
void CacheHitUTest::test_hit(void)
{
	logger().debug("BEGIN TEST: %s", __FUNCTION__);

	for (char c='0'; c<'Z'; c++)
	{
		printf("Start iteration %c\n", c);
		as->clear();
		eval->eval(
		"  (EvaluationLink"
		"    (PredicateNode \"*-word-shape pair-*\")"
		"    (Concept \"b\")"
		"    (EvaluationLink"
		"      (PredicateNode \"*-shape-*\")"
		"      (Concept \"b\")"
		"      (Connector (Concept \"###LEFT-WALL###\") (Sex \"-\"))"
		"      (Connector (VariableNode \"$connector-word\") (Sex \"-\"))"
		"      (Connector (Concept \"a\") (Sex \"+\"))))"
		""
		"  (define var (Variable \"$api-right-star\"))"
		"  (define term "
		"    (EvaluationLink"
		"      (PredicateNode \"*-word-shape pair-*\")"
		"      (ConceptNode \"b\")"
		"      (VariableNode \"$api-right-star\")))"
		""
		"  (define b (CollectionOf (Query (TypedVariable var (Type \"Evaluation\")) term term)))"
		);

		for (char j='0'; j<c; j++)
			add_cruft(j);

		Handle crash = eval->eval_h("(cog-execute! b)");

		// std::cout << "Answer: " << crash->to_string() << std::endl;
		TS_ASSERT_EQUALS(1, crash->get_arity());

		// Create the expected answer, and compare
		Handle expected = eval->eval_h(
		"(SetLink"
		"  (EvaluationLink"
		"    (PredicateNode \"*-word-shape pair-*\")"
		"    (ConceptNode \"b\")"
		"    (EvaluationLink"
		"      (PredicateNode \"*-shape-*\")"
		"      (ConceptNode \"b\")"
		"      (Connector (ConceptNode \"###LEFT-WALL###\") (Sex \"-\"))"
		"      (Connector (VariableNode \"$connector-word\") (Sex \"-\"))"
		"      (Connector (ConceptNode \"a\") (Sex \"+\")))))"
		);
		TS_ASSERT_EQUALS(expected, crash);
	}

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