/*
* tests/persist/cog/BasicSaveUTest.cxxtest
*
* Most basic, simplest sniff test, saves and restores a few atoms.
* Copied from tests/persist/sql/BasicSaveUTest.cxxtest and
* lightly modified.
*
* Copyright (C) 2008, 2009, 2019 Linas Vepstas <linasvepstas@gmail.com>
*
* LICENSE:
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* 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 <opencog/atoms/base/Atom.h>
#include <opencog/atoms/base/Link.h>
#include <opencog/atoms/base/Node.h>
#include <opencog/atoms/atom_types/atom_types.h>
#include <opencog/atoms/value/FloatValue.h>
#include <opencog/atomspace/AtomSpace.h>
#include "../TestCogServer.h"
#include <opencog/persist/cog-storage/CogStorage.h>
#include <opencog/util/Logger.h>
using namespace opencog;
class BasicSaveUTest : public CxxTest::TestSuite
{
private:
std::string uri;
DECLARE_TEST_COGSERVER
Handle n1[10];
Handle n2[10];
Handle n3[10];
Handle n4[10];
Handle l[10];
Handle a1[10];
Handle a2[10];
Handle a3[10];
Handle a4[10];
Handle al[10];
public:
BasicSaveUTest(void)
{
logger().set_level(Logger::INFO);
// logger().set_level(Logger::DEBUG);
logger().set_print_to_stdout_flag(true);
uri = "cog://localhost:16001";
INIT_TEST_COGSERVER(16001);
printf("Started CogServer\n");
}
~BasicSaveUTest()
{
STOP_TEST_COGSERVER
// 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 single_atom_save_restore(std::string id);
void add_to_table(int, AtomSpace *, std::string id);
void check_table(int, AtomSpace *, std::string id);
void test_single_atom(void);
void test_fresh_atom(void);
void test_table(void);
};
/*
* This is called once before each test, for each test (!!)
*/
void BasicSaveUTest::setUp(void)
{
}
void BasicSaveUTest::tearDown(void)
{
}
// ============================================================
static void atomCompare(Handle a, AtomPtr b, const char* where)
{
printf("Testing at %s\n", where);
TSM_ASSERT("No atom found", b != nullptr);
if (nullptr == b) return;
TSM_ASSERT_EQUALS("Type mismatch", a->get_type(), b->get_type());
NodePtr na(NodeCast(a));
NodePtr nb(NodeCast(b));
if (na and nb)
{
TSM_ASSERT_EQUALS("Name mismatch", na->get_name(), nb->get_name());
}
LinkPtr la(LinkCast(a));
LinkPtr lb(LinkCast(b));
if (la and lb)
{
TSM_ASSERT_EQUALS("Arity mismatch", la->get_arity(), lb->get_arity());
if (*la != *lb)
{
printf("Mismatching la = %s\n", la->to_string().c_str());
printf("Mismatching lb = %s\n", lb->to_string().c_str());
TSM_ASSERT_EQUALS("Link mis-match", la, lb);
}
}
ValuePtr ta = a->getValue(truth_key());
ValuePtr tb = b->getValue(truth_key());
if (ta or tb)
{
TSM_ASSERT("Missing truth value", ta);
TSM_ASSERT("Missing truth value", tb);
if (ta and tb)
{
TSM_ASSERT("Truth value miscompare", (*ta)==(*tb));
if (not ((*ta) == (*tb)))
{
FloatValuePtr fva = FloatValueCast(ta);
FloatValuePtr fvb = FloatValueCast(tb);
fprintf(stderr,
"Error, truth value miscompare, ma=%18.16g\n"
" mb=%18.16g\n"
" mdiff=%g ca=%g cb=%g\n",
fva->value()[0], fvb->value()[0],
fva->value()[0] - fvb->value()[0],
fva->value()[1], fvb->value()[1]);
fprintf(stderr, "Above is for %s\n\n", a->to_string().c_str());
}
}
}
}
// ============================================================
/**
* A simple test case that tests the save and restore of
* a couple of nodes and a link. Does not test atomtable/atomspace at all.
*/
void BasicSaveUTest::single_atom_save_restore(std::string id)
{
CogStorage *store = new CogStorage(uri);
store->open();
if (!store->connected())
{
logger().debug("single_atom_save_restore: cannot connect to storage");
TSM_ASSERT("cannot connect to storage", false);
return;
}
// Create an atom ...
Handle a(createNode(SCHEMA_NODE, id + "someNode"));
ValuePtr stv(createFloatValue(std::vector<double>({0.55, 0.6})));
a->setValue(truth_key(), stv);
Handle h = a->get_handle();
TSM_ASSERT("Bad Handle", nullptr != h);
// Store the atom ...
store->storeAtom(h, true);
store->barrier();
printf("Done storing single node\n");
// Fetch it back ...
std::string nam = id + "someNode";
Handle b = createNode(SCHEMA_NODE, nam.c_str());
store->getAtom(b);
store->barrier();
printf("Done fetching single node\n");
// Are they equal ??
atomCompare(a, b, "Single node save-restore");
// Create a second atom, connect it to the first
// with a link. Save it, fetch it ... are they equal?
Handle a2(createNode(SCHEMA_NODE, id + "otherNode"));
store->storeAtom(a2->get_handle(), true);
HandleSeq hvec;
hvec.push_back(a->get_handle());
hvec.push_back(a2->get_handle());
Handle l(createLink(hvec, LIST_LINK));
store->storeAtom(l->get_handle(), true);
store->barrier();
printf("Done storing single link\n");
Handle hl = createLink(LIST_LINK, a->get_handle(), a2->get_handle());
store->getAtom(hl);
store->barrier();
atomCompare(l, hl, "Single link save-restore");
printf("Done fetching single link\n");
kill_data(store, _test_asp.get());
store->close();
delete store;
}
// ============================================================
void BasicSaveUTest::add_to_table(int idx, AtomSpace *table, std::string id)
{
// Create an atom ...
n1[idx] = createNode(SCHEMA_NODE, id + "fromNode");
ValuePtr stv(createFloatValue(std::vector<double>({0.11, 1.0/(100.0+idx)})));
n1[idx]->setValue(truth_key(), stv);
n1[idx] = table->add_atom(n1[idx]);
a1[idx] = n1[idx];
n2[idx] = createNode(SCHEMA_NODE, id + "toNode");
ValuePtr stv2(createFloatValue(std::vector<double>({0.22, 1.0/(200.0+idx)})));
n2[idx]->setValue(truth_key(), stv2);
n2[idx] = table->add_atom(n2[idx]);
a2[idx] = n2[idx];
// ODBC fails when there is a question mark in the text.
// Sad but painfully true.
std::string qmark = "? ";
n3[idx] = createNode(SCHEMA_NODE, qmark + id + "third ? wheel");
// We need to test crazy-large and crazy-small float point values.
ValuePtr stv3(createFloatValue(std::vector<double>({1.0e33/3.0, (1.0e-30)/(300.0+idx)})));
n3[idx]->setValue(truth_key(), stv3);
n3[idx] = table->add_atom(n3[idx]);
a3[idx] = n3[idx];
// The NumberNode will go through the AtomSpace clone factory
// and should thus elicit any errors in clone uuid handling.
char buf[40]; sprintf(buf, "%f", idx+0.14159265358979);
n4[idx] = createNode(NUMBER_NODE, buf);
// We need to test crazy-large and crazy-small float point values.
ValuePtr stv4(createFloatValue(std::vector<double>({(4.0e-200)/9.0, 1.0e40/(400.0+idx)})));
n4[idx]->setValue(truth_key(), stv4);
n4[idx] = table->add_atom(n4[idx]);
a4[idx] = n4[idx];
HandleSeq hvec;
hvec.push_back(a1[idx]->get_handle());
hvec.push_back(a2[idx]->get_handle());
hvec.push_back(a3[idx]->get_handle());
hvec.push_back(a4[idx]->get_handle());
l[idx] = createLink(hvec, SET_LINK);
l[idx] = table->add_atom(l[idx]);
al[idx] = l[idx];
}
void BasicSaveUTest::check_table(int idx, AtomSpace *table, std::string id)
{
Handle hb1 = table->get_atom(Handle(n1[idx]));
atomCompare(a1[idx], hb1, "check_table hb1");
Handle hb2 = table->get_atom(Handle(n2[idx]));
atomCompare(a2[idx], hb2, "check_table hb2");
Handle hb3 = table->get_atom(Handle(n3[idx]));
atomCompare(a3[idx], hb3, "check_table hb3");
Handle hb4 = table->get_atom(Handle(n4[idx]));
atomCompare(a4[idx], hb4, "check_table hb4");
HandleSeq hvec;
hvec.push_back(hb1);
hvec.push_back(hb2);
hvec.push_back(hb3);
hvec.push_back(hb4);
Handle lb(createLink(hvec, SET_LINK));
Handle hlb = table->get_atom(lb);
atomCompare(al[idx], hlb, "check_table hlb");
}
// ============================================================
void BasicSaveUTest::test_single_atom(void)
{
logger().debug("BEGIN TEST: %s", __FUNCTION__);
// Trash the contents of the database.
single_atom_save_restore("aaa ");
single_atom_save_restore("bbb ");
single_atom_save_restore("ccc ");
single_atom_save_restore("ddd ");
single_atom_save_restore("eee ");
single_atom_save_restore("$$$ ");
logger().debug("END TEST: %s", __FUNCTION__);
}
void BasicSaveUTest::test_fresh_atom(void)
{
logger().debug("BEGIN TEST: %s", __FUNCTION__);
CogStorage *store = new CogStorage(uri);
store->open();
if (!store->connected())
{
logger().debug("test_fresh_atom: cannot connect to db");
return;
}
Handle ha(createNode(CONCEPT_NODE, "electrical"));
Handle hb(createNode(CONCEPT_NODE, "energy"));
// Store a ListLink with stuff in it.
Handle lf(createLink(LIST_LINK, ha, hb));
lf->setValue(truth_key(), createFloatValue(std::vector<double>({0.55, 0.6})));
store->storeAtom(lf, true);
// Store an empty ListLink.
Handle le(createLink(HandleSeq(), LIST_LINK));
le->setValue(truth_key(), createFloatValue(std::vector<double>({0.1, 0.9})));
store->storeAtom(le, true);
store->barrier();
// Now get them back. Get empty after full.
Handle hf(createLink(LIST_LINK, ha, hb));
store->getAtom(hf);
store->barrier();
atomCompare(lf, hf, "Fully Fresh");
Handle he(createLink(LIST_LINK));
store->getAtom(he);
store->barrier();
printf("Expecting %s\n", le->to_string().c_str());
if (he) printf("Got %s\n", he->to_string().c_str());
atomCompare(le, he, "Empty Fresh");
kill_data(store, _test_asp.get());
store->close();
delete store;
logger().debug("END TEST: %s", __FUNCTION__);
}
void BasicSaveUTest::test_table()
{
logger().debug("BEGIN TEST: %s", __FUNCTION__);
CogStorage *store = new CogStorage(uri);
store->open();
if (!store->connected())
{
logger().debug("test_table: cannot connect to db");
return;
}
AtomSpacePtr as1 = createAtomSpace();
AtomSpace *table1 = as1.get();
int idx = 0;
add_to_table(idx++, table1, "AA-aa-wow ");
add_to_table(idx++, table1, "BB-bb-wow ");
add_to_table(idx++, table1, "CC-cc-wow ");
add_to_table(idx++, table1, "DD-dd-wow ");
add_to_table(idx++, table1, "EE-ee-wow ");
store->storeAtomSpace(table1);
size_t num_stored = table1->get_size();
// table1->print();
#ifdef DOPRT
HandleSeq hset;
table1->get_handles_by_type(hset, ATOM, true, false);
for (auto h: hset) {
printf("Store table has %s\n", h->to_string().c_str());
}
#endif
store->close();
delete store;
as1 = nullptr;
printf("Done storing table; size=%lu\n", num_stored);
// Reopen connection, and load the atom table.
store = new CogStorage(uri);
store->open();
TSM_ASSERT("Not connected to database", store->connected());
AtomSpacePtr as2 = createAtomSpace();
AtomSpace *table2 = as2.get();
store->loadAtomSpace(table2);
store->barrier();
// table2->print();
size_t num_fetched = table2->get_size();
printf("test_table: Done loading table; fetched %lu, expected %lu\n",
num_fetched, num_stored);
// 12 extra: (PredicateNode "*-TruthValueKey-*") and "*-IsKeyFlag-*"
// and gorp from CogServerNode
TSM_ASSERT("Wrong number of atoms fetched", num_fetched == num_stored+12);
idx = 0;
check_table(idx++, table2, "aaa ");
check_table(idx++, table2, "bbb ");
check_table(idx++, table2, "ccc ");
check_table(idx++, table2, "ddd ");
check_table(idx++, table2, "eee ");
kill_data(store, _test_asp.get());
store->close();
delete store;
logger().debug("END TEST: %s", __FUNCTION__);
}
/* ============================= END OF FILE ================= */