OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace-cog / tests / persist / cog-storage / BasicSaveUTest.cxxtest

438 lines · cxxtest ref:
/*
 * 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 ================= */