/*
* tests/persist/rocks/AlphaEquivUTest.cxxtest
*
* Test alpha-equivalence. Assumes PerisistUTest and ValueSaveUTest
* are passing.
*
* Copyright (C) 2008, 2009, 2013, 2020 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 <cstdio>
#include <filesystem>
#include <opencog/atoms/atom_types/atom_types.h>
#include <opencog/atoms/base/Atom.h>
#include <opencog/atoms/base/Link.h>
#include <opencog/atoms/base/Node.h>
#include <opencog/atoms/value/FloatValue.h>
#include <opencog/atoms/value/VoidValue.h>
#include <opencog/atomspace/AtomSpace.h>
#include <opencog/persist/rocks-types/atom_types.h>
#include <opencog/persist/rocks/RocksStorage.h>
#include <opencog/util/Logger.h>
using namespace opencog;
class AlphaEquivUTest : public CxxTest::TestSuite
{
private:
AtomSpacePtr _as;
std::string uri;
public:
AlphaEquivUTest(void);
~AlphaEquivUTest()
{
// erase the log file if no assertions failed
if (!CxxTest::TestTracker::tracker().suiteFailed())
{
std::remove(logger().get_filename().c_str());
// Also remove the database directory
// URI is "rocks:///tmp/..." so path starts at position 8
std::string dbpath = uri.substr(8);
std::filesystem::remove_all(dbpath);
}
}
void setUp(void);
void tearDown(void);
void kill_data(void);
void setup_data(void);
void test_fetch(void);
void test_bulk_load(void);
void test_remove(void);
void test_recursive_remove(void);
};
AlphaEquivUTest:: AlphaEquivUTest(void)
{
logger().set_level(Logger::DEBUG);
logger().set_print_to_stdout_flag(true);
uri = "rocks:///tmp/cog-rocks-alpha-equiv-utest";
}
/*
* This is called once before each test, for each test (!!)
*/
void AlphaEquivUTest::setUp(void)
{
_as = nullptr;
}
void AlphaEquivUTest::tearDown(void)
{
kill_data();
}
// ============================================================
void AlphaEquivUTest::kill_data(void)
{
RocksStorage *astore = new RocksStorage(uri);
astore->open();
if (!astore->connected())
{
logger().info("setUp: RocksStorage cannot connect to database");
exit(1);
}
// Trash the contents of the database.
astore->kill_data();
// Destructor also logs out of database
delete astore;
}
// ============================================================
#define an _as->add_node
#define al _as->add_link
#define CONCEPT CONCEPT_NODE
#define PREDICATE PREDICATE_NODE
#define VARIABLE VARIABLE_NODE
void AlphaEquivUTest::setup_data(void)
{
kill_data();
_as = createAtomSpace();
Handle hsn = _as->add_node(ROCKS_STORAGE_NODE, std::string(uri));
StorageNodePtr store = StorageNodeCast(hsn);
store->setValue(_as->add_node(PREDICATE_NODE, "*-open-*"), createVoidValue());
TS_ASSERT(store->connected())
/* Populate the Storage with stuff that we will later query. */
Handle lx = al(LAMBDA_LINK, an(VARIABLE, "X"), an(CONCEPT, "A"));
lx->setValue(truth_key(), createFloatValue(std::vector<double>({0.11, 100})));
lx->setValue(an(PREDICATE, "foo"),
createFloatValue(std::vector<double>{1, 2, 3}));
/* Push all atoms out to the database */
store->store_atomspace();
store->barrier();
_as = nullptr;
}
// ============================================================
// Test fetching by an alpha-equivalent name
void AlphaEquivUTest::test_fetch(void)
{
logger().info("BEGIN TEST: %s", __FUNCTION__);
setup_data();
_as = createAtomSpace();
Handle hsn = _as->add_node(ROCKS_STORAGE_NODE, std::string(uri));
StorageNodePtr store = StorageNodeCast(hsn);
store->setValue(_as->add_node(PREDICATE_NODE, "*-open-*"), createVoidValue());
TS_ASSERT(store->connected())
// Fetch an alpha-equivalent lambda link.
Handle ly = al(LAMBDA_LINK, an(VARIABLE, "Y"), an(CONCEPT, "A"));
ly = store->fetch_atom(ly);
store->barrier();
// Verify that the correct TV was fetched.
ValuePtr tvp = ly->getValue(truth_key());
printf("Got TV: %s\n", tvp->to_string().c_str());
FloatValuePtr fv = FloatValueCast(tvp);
TSM_ASSERT("Expecting (stv 0.11 100)",
0.1 < fv->value()[0] and fv->value()[0] < 0.12 and
99 < fv->value()[1] and fv->value()[1] < 101);
// Verify that other values are there too.
ValuePtr fu = ly->getValue(an(PREDICATE, "foo"));
printf("Got Value: %s\n", fu->to_string().c_str());
TSM_ASSERT("Expecting a LinkValue",
nameserver().isA(fu->get_type(), FLOAT_VALUE));
FloatValuePtr fvp = FloatValueCast(fu);
TSM_ASSERT_EQUALS("Expecting three floats", fvp->size(), 3);
_as = nullptr;
logger().info("END TEST: %s", __FUNCTION__);
}
// ============================================================
// Test bulk loading of alpha-convertible atoms via load_atomspace().
// This is a regression test for a bug where the h@hash prefix stored
// with alpha-convertible atoms was not stripped during bulk load,
// causing parse errors. Individual fetch_atom() worked correctly
// because getAtom() handled the prefix, but loadAtoms() did not.
void AlphaEquivUTest::test_bulk_load(void)
{
logger().info("BEGIN TEST: %s", __FUNCTION__);
setup_data();
_as = createAtomSpace();
Handle hsn = _as->add_node(ROCKS_STORAGE_NODE, std::string(uri));
StorageNodePtr store = StorageNodeCast(hsn);
store->setValue(_as->add_node(PREDICATE_NODE, "*-open-*"), createVoidValue());
TS_ASSERT(store->connected())
// Bulk load the entire AtomSpace (this was failing before the fix)
store->load_atomspace();
store->barrier();
// Find the alpha-equivalent lambda link that was loaded
Handle ly = al(LAMBDA_LINK, an(VARIABLE, "Y"), an(CONCEPT, "A"));
// Verify that the correct TV was loaded
ValuePtr tvp = ly->getValue(truth_key());
TSM_ASSERT("Expecting non-null TV after bulk load", tvp != nullptr);
if (tvp)
{
printf("Got TV: %s\n", tvp->to_string().c_str());
FloatValuePtr fv = FloatValueCast(tvp);
TSM_ASSERT("Expecting (stv 0.11 100)",
0.1 < fv->value()[0] and fv->value()[0] < 0.12 and
99 < fv->value()[1] and fv->value()[1] < 101);
}
// Verify that other values are there too
ValuePtr fu = ly->getValue(an(PREDICATE, "foo"));
TSM_ASSERT("Expecting non-null value after bulk load", fu != nullptr);
if (fu)
{
printf("Got Value: %s\n", fu->to_string().c_str());
TSM_ASSERT("Expecting a FloatValue",
nameserver().isA(fu->get_type(), FLOAT_VALUE));
FloatValuePtr fvp = FloatValueCast(fu);
TSM_ASSERT_EQUALS("Expecting three floats", fvp->size(), 3);
}
_as = nullptr;
logger().info("END TEST: %s", __FUNCTION__);
}
// ============================================================
// Test removal by an alpha-equivalent name
void AlphaEquivUTest::test_remove(void)
{
logger().info("BEGIN TEST: %s", __FUNCTION__);
setup_data();
_as = createAtomSpace();
Handle hsn = _as->add_node(ROCKS_STORAGE_NODE, std::string(uri));
StorageNodePtr store = StorageNodeCast(hsn);
store->setValue(_as->add_node(PREDICATE_NODE, "*-open-*"), createVoidValue());
TS_ASSERT(store->connected())
// Remove the alpha-equivalent lambda link.
Handle ly = al(LAMBDA_LINK, an(VARIABLE, "Y"), an(CONCEPT, "A"));
store->remove_atom(_as.get(), ly);
store->barrier();
ly = Handle::UNDEFINED;
// Can we still find the original?
Handle lx = al(LAMBDA_LINK, an(VARIABLE, "X"), an(CONCEPT, "A"));
lx = store->fetch_atom(lx);
store->barrier();
ValuePtr tvp = lx->getValue(truth_key());
TSM_ASSERT("Expecting default TV", tvp == nullptr);
// Verify that other values are absent
ValuePtr fu = lx->getValue(an(PREDICATE, "foo"));
TSM_ASSERT("Expecting null value", nullptr == fu);
_as = nullptr;
logger().info("END TEST: %s", __FUNCTION__);
}
// ============================================================
// Test recursive removal of alpha-equivalences
void AlphaEquivUTest::test_recursive_remove(void)
{
logger().info("BEGIN TEST: %s", __FUNCTION__);
setup_data();
_as = createAtomSpace();
Handle hsn = _as->add_node(ROCKS_STORAGE_NODE, std::string(uri));
StorageNodePtr store = StorageNodeCast(hsn);
store->setValue(_as->add_node(PREDICATE_NODE, "*-open-*"), createVoidValue());
TS_ASSERT(store->connected())
// Recursive remove Concept "A".
Handle ca = an(CONCEPT, "A");
store->remove_atom(_as.get(), ca, true);
store->barrier();
// What happens if we look for the original?
Handle lx = al(LAMBDA_LINK, an(VARIABLE, "X"), an(CONCEPT, "A"));
lx = store->fetch_atom(lx);
store->barrier();
ValuePtr tvp = lx->getValue(truth_key());
TSM_ASSERT("Expecting default TV", tvp == nullptr);
// Verify that other values are absent
ValuePtr fu = lx->getValue(an(PREDICATE, "foo"));
TSM_ASSERT("Expecting null value", nullptr == fu);
_as = nullptr;
logger().info("END TEST: %s", __FUNCTION__);
}
/* ============================= END OF FILE ================= */