OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace-cog / tests / persist / cog-simple / SimpleDeleteUTest.cxxtest

588 lines · cxxtest ref:
/*
 * tests/persist/cog-simple/DeleteUTest.cxxtest
 *
 * Test deletion from backing store.  Assumes PersistUTest is passing.
 * Ported from tests/persist/sql/multi-driver/DeleteUTest.cxxtest
 *
 * Copyright (C) 2008, 2009, 2013, 2017, 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/atom_types/atom_types.h>
#include <opencog/persist/cog-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/persist/cog-simple/CogSimpleStorage.h>

#include <opencog/util/Logger.h>
#include "../TestCogServer.h"

using namespace opencog;

class DeleteUTest :  public CxxTest::TestSuite
{
    private:
        AtomSpacePtr _as;
        DECLARE_TEST_COGSERVER

        NodePtr n1[10];
        NodePtr n2[10];
        NodePtr n3[10];
        NodePtr n4[10];
        LinkPtr l[10];
        LinkPtr l2[10];
        LinkPtr l3[10];
        Handle h1[10];
        Handle h2[10];
        Handle h3[10];
        Handle h4[10];
        Handle hl[10];
        Handle hl2[10];
        Handle hl3[10];

    public:

        DeleteUTest(void);
        ~DeleteUTest()
        {
            STOP_TEST_COGSERVER

            logger().flush();

            // 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_to_space(int, AtomSpacePtr , std::string id);
        void fetch_space(int, StorageNodePtr);
        void check_space(int, AtomSpacePtr , std::string dbgmsg);
        void check_remove(int, AtomSpacePtr , std::string dbgmsg);
        void check_empty(int, AtomSpacePtr );
        void extract_links(int, AtomSpacePtr );
        void delete_links(int, StorageNodePtr);
        void extract_nodes(int, AtomSpacePtr );
        void delete_nodes(int, StorageNodePtr);
        void atomCompare(Handle, AtomPtr, std::string);
        void tvMiscompare(Handle, AtomPtr, std::string);

        void test_remove(void);

        void test_recurse(void);
};

DeleteUTest:: DeleteUTest(void)
{
    // logger().set_level(Logger::DEBUG);
    logger().set_level(Logger::INFO);
    logger().set_print_to_stdout_flag(true);

    INIT_TEST_COGSERVER(16305);
    printf("Started CogServer\n");
}

/*
 * This is called once before each test, for each test (!!)
 */
void DeleteUTest::setUp(void)
{
    _as = createAtomSpace();
}

void DeleteUTest::tearDown(void)
{
    _as = nullptr;
}

// ============================================================

void DeleteUTest::atomCompare(Handle a, AtomPtr b, std::string where)
{
    logger().debug("Check %s expect %s", where.c_str(), a->to_string().c_str());
    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)
        {
            if (not ((*ta) == (*tb)))
            {
                FloatValuePtr fva = FloatValueCast(ta);
                FloatValuePtr fvb = FloatValueCast(tb);
                printf("Error, truth value miscompare on %s\n"
                    "\tma=%f mb=%f ca=%f cb=%f\n",
                    where.c_str(),
                    fva->value()[0], fvb->value()[0],
                    fva->value()[1], fvb->value()[1]);
            }
            TSM_ASSERT("Truth value miscompare", (*ta)==(*tb));
        }
    }
}

// Passes only if the TV's are different!
void DeleteUTest::tvMiscompare(Handle a, AtomPtr b, std::string where)
{
    if (nullptr == a or nullptr == b) return;

    ValuePtr ta = a->getValue(truth_key());
    ValuePtr tb = b->getValue(truth_key());
    if (ta and tb)
    {
        TSM_ASSERT("Truth value should not match! " + where, (*ta)!=(*tb));
    }
    if (nullptr==ta and nullptr==tb)
    {
        TSM_ASSERT("Truth value null!! " + where, false);
    }
}

// ============================================================

void DeleteUTest::add_to_space(int idx, AtomSpacePtr as, std::string id)
{
    // Create an atom ...
    ValuePtr stv(createFloatValue(std::vector<double>({0.11, 100.0+idx})));
    h1[idx] = as->add_node(SCHEMA_NODE, id + "fromNode");
    h1[idx]->setValue(truth_key(), stv);
    n1[idx] = NodeCast(h1[idx]);

    ValuePtr stv2(createFloatValue(std::vector<double>({0.22, 200.0+idx})));
    h2[idx] = as->add_node(SCHEMA_NODE, id + "toNode");
    h2[idx]->setValue(truth_key(), stv2);
    n2[idx] = NodeCast(h2[idx]);

    ValuePtr stv3(createFloatValue(std::vector<double>({0.33, 300.0+idx})));
    h3[idx] = as->add_node(SCHEMA_NODE, id + "third wheel");
    h3[idx]->setValue(truth_key(), stv3);
    n3[idx] = NodeCast(h3[idx]);

    // The NumberNode will go through the AtomTable clone factory
    // and should thus elicit any errors in clone uuid handling.
    char buf[40]; sprintf(buf, "%f", idx+0.14159265358979);
    h4[idx] = as->add_node(NUMBER_NODE, buf);
    ValuePtr stv4(createFloatValue(std::vector<double>({0.44, 400.0+idx})));
    h4[idx]->setValue(truth_key(), stv4);
    n4[idx] = NodeCast(h4[idx]);

    HandleSeq hvec;
    hvec.push_back(h1[idx]);
    hvec.push_back(h2[idx]);
    hvec.push_back(h3[idx]);
    hvec.push_back(h4[idx]);

    // Note that SetLink is an unordered link.
    ValuePtr stv5(createFloatValue(std::vector<double>({0.55, 500.0+idx})));
    hl[idx] = as->add_link(SET_LINK, std::move(hvec));
    hl[idx]->setValue(truth_key(), stv5);
    l[idx] = LinkCast(hl[idx]);

    ValuePtr stv6(createFloatValue(std::vector<double>({0.66, 600.0+idx})));
    hl2[idx] = as->add_link(LIST_LINK, hl[idx], h2[idx]);
    hl2[idx]->setValue(truth_key(), stv6);
    l2[idx] = LinkCast(hl2[idx]);

    ValuePtr stv7(createFloatValue(std::vector<double>({0.77, 700.0+idx})));
    hl3[idx] = as->add_link(EVALUATION_LINK, h1[idx], hl2[idx], h3[idx]);
    hl3[idx]->setValue(truth_key(), stv7);
    l3[idx] = LinkCast(hl3[idx]);
}

void DeleteUTest::fetch_space(int idx, StorageNodePtr space)
{
    Handle ab1 = createNode(n1[idx]->get_type(), n1[idx]->get_name());
    Handle hb1(ab1->get_handle());
    space->fetch_atom(hb1);

    Handle ab2 = createNode(n2[idx]->get_type(), n2[idx]->get_name());
    Handle hb2(ab2->get_handle());
    space->fetch_atom(hb2);

    Handle ab3 = createNode(n3[idx]->get_type(), n3[idx]->get_name());
    Handle hb3(ab3->get_handle());
    space->fetch_atom(hb3);

    Handle ab4 = createNode(n4[idx]->get_type(), n4[idx]->get_name());
    Handle hb4(ab4->get_handle());
    space->fetch_atom(hb4);

    HandleSeq hvec;
    hvec.push_back(hb1);
    hvec.push_back(hb2);
    hvec.push_back(hb3);
    hvec.push_back(hb4);

    Handle alb = createLink(hvec, hl[idx]->get_type());
    Handle hlb(alb->get_handle());

    Handle flb = space->fetch_atom(hlb);

    HandleSeq hv2({hlb, hb2});
    Handle alb2 = createLink(hv2, hl2[idx]->get_type());
    Handle hlb2(alb2->get_handle());
    space->fetch_atom(hlb2);

    HandleSeq hv3({hb1, hlb2, hb3});
    Handle alb3 = createLink(hv3, hl3[idx]->get_type());
    Handle hlb3(alb3->get_handle());
    space->fetch_atom(hlb3);
}

void DeleteUTest::check_space(int idx, AtomSpacePtr space, std::string dbgmsg)
{
    dbgmsg += "-idx=" + std::to_string(idx);
    std::string name1 = n1[idx]->get_name();
    Handle hb1 = space->get_node(n1[idx]->get_type(), std::move(name1));
    atomCompare(h1[idx], hb1, dbgmsg + "-hb1");

    std::string name2 = n2[idx]->get_name();
    Handle hb2 = space->get_node(n2[idx]->get_type(), std::move(name2));
    atomCompare(h2[idx], hb2, dbgmsg + "-hb2");

    std::string name3 = n3[idx]->get_name();
    Handle hb3 = space->get_node(n3[idx]->get_type(), std::move(name3));
    atomCompare(h3[idx], hb3, dbgmsg + "-hb3");

    std::string name4 = n4[idx]->get_name();
    Handle hb4 = space->get_node(n4[idx]->get_type(), std::move(name4));
    atomCompare(h4[idx], hb4, dbgmsg + "-hb4");

    HandleSeq hvec;
    hvec.push_back(hb1);
    hvec.push_back(hb2);
    hvec.push_back(hb3);
    hvec.push_back(hb4);

    Handle hlb = space->get_link(hl[idx]->get_type(), std::move(hvec));
    atomCompare(hl[idx], hlb, dbgmsg + "-hlinkb");

    HandleSeq hv2({hlb, hb2});
    Handle hlb2 = space->get_link(hl2[idx]->get_type(), std::move(hv2));
    atomCompare(hl2[idx], hlb2, dbgmsg + "-hlinkb2");

    HandleSeq hv3({hb1, hlb2, hb3});
    Handle hlb3 = space->get_link(hl3[idx]->get_type(), std::move(hv3));
    atomCompare(hl3[idx], hlb3, dbgmsg + "-hlinkb3");
}

void DeleteUTest::check_remove(int idx, AtomSpacePtr space, std::string dbgmsg)
{
    std::string name1 = n1[idx]->get_name();
    Handle hb1 = space->get_node(n1[idx]->get_type(), std::move(name1));
    std::string name2 = n2[idx]->get_name();
    Handle hb2 = space->get_node(n2[idx]->get_type(), std::move(name2));
    std::string name3 = n3[idx]->get_name();
    Handle hb3 = space->get_node(n3[idx]->get_type(), std::move(name3));
    std::string name4 = n4[idx]->get_name();
    Handle hb4 = space->get_node(n4[idx]->get_type(), std::move(name4));

    HandleSeq hvec;
    hvec.push_back(hb1);
    hvec.push_back(hb2);
    hvec.push_back(hb3);
    hvec.push_back(hb4);

    Handle hlb = space->get_link(hl[idx]->get_type(), std::move(hvec));
    tvMiscompare(hl[idx], hlb, dbgmsg + "-hlinkb");

    HandleSeq hv2({hlb, hb2});
    Handle hlb2 = space->get_link(hl2[idx]->get_type(), std::move(hv2));
    tvMiscompare(hl2[idx], hlb2, dbgmsg + "-hlinkb2");

    HandleSeq hv3({hb1, hlb2, hb3});
    Handle hlb3 = space->get_link(hl3[idx]->get_type(), std::move(hv3));
    tvMiscompare(hl3[idx], hlb3, dbgmsg + "-hlinkb3");
}

void DeleteUTest::check_empty(int idx, AtomSpacePtr space)
{
    std::string name1 = n1[idx]->get_name();
    Handle hb1 = space->get_node(n1[idx]->get_type(), std::move(name1));
    TSM_ASSERT("Should not find this atom", hb1 == Handle::UNDEFINED);

    std::string name2 = n2[idx]->get_name();
    Handle hb2 = space->get_node(n2[idx]->get_type(), std::move(name2));
    TSM_ASSERT("Should not find this atom", hb2 == Handle::UNDEFINED);

    std::string name3 = n3[idx]->get_name();
    Handle hb3 = space->get_node(n3[idx]->get_type(), std::move(name3));
    TSM_ASSERT("Should not find this atom", hb3 == Handle::UNDEFINED);

    std::string name4 = n4[idx]->get_name();
    Handle hb4 = space->get_node(n4[idx]->get_type(), std::move(name4));
    TSM_ASSERT("Should not find this atom", hb4 == Handle::UNDEFINED);

    HandleSeq hvec;
    hvec.push_back(h1[idx]);
    hvec.push_back(h2[idx]);
    hvec.push_back(h3[idx]);
    hvec.push_back(h4[idx]);

    Handle hlb = space->get_link(hl[idx]->get_type(), std::move(hvec));
    TSM_ASSERT("Should not find this atom", hlb == Handle::UNDEFINED);
}

// This removes from atomspace, but not from the DB.
void DeleteUTest::extract_links(int idx, AtomSpacePtr as)
{
	as->extract_atom(hl3[idx]);
	as->extract_atom(hl2[idx]);
	as->extract_atom(hl[idx]);
}

// This should delete all the links from the database (and also
// the AtomSpace)
void DeleteUTest::delete_links(int idx, StorageNodePtr store)
{
	store->remove_atom(_as, hl3[idx]);
	store->remove_atom(_as, hl2[idx]);
	store->remove_atom(_as, hl[idx]);
	_as->extract_atom(hl3[idx]);
	_as->extract_atom(hl2[idx]);
	_as->extract_atom(hl[idx]);
}

// This removes all the links, and one node, from atomspace,
// but not from the DB.
void DeleteUTest::extract_nodes(int idx, AtomSpacePtr as)
{
	as->extract_atom(h1[idx], true);
}

// This should delete all the links and one node from the database.
void DeleteUTest::delete_nodes(int idx, StorageNodePtr store)
{
	store->remove_atom(_as, h3[idx], true);
	_as->extract_atom(h3[idx], true);
}

// ============================================================

void DeleteUTest::test_remove(void)
{
    logger().debug("BEGIN TEST: %s", __FUNCTION__);

    Handle hsn = _as->add_node(COG_SIMPLE_STORAGE_NODE, "cog://localhost:16305");
    StorageNodePtr store = StorageNodeCast(hsn);
    store->setValue(_as->add_node(PREDICATE_NODE, "*-open-*"), createVoidValue());
    TS_ASSERT(store->connected())

    int idx = 0;
    add_to_space(idx++, _as, "AA-aa-wow ");
    add_to_space(idx++, _as, "BB-bb-wow ");
    add_to_space(idx++, _as, "CC-cc-wow ");
    add_to_space(idx++, _as, "DD-dd-wow ");
    add_to_space(idx++, _as, "EE-ee-wow ");

    /* Make sure UTF-8 works fine. */
    add_to_space(idx++, _as, "Попытка выбраться вызвала слабый стон ");
    add_to_space(idx++, _as, "はにがうりだそうであってるのかはち ");
    add_to_space(idx++, _as, "係拉丁字母");

    /* Verify that the atoms were added */
    int i;
    for (i=0; i<idx; i++)
        check_space(i, _as, "verify-add");

    /* Push all atoms out to storage */
    store->store_atomspace();

    // +1 for (Predicate "*-IsKeyFlag-*"), +1 for *-open-*
    printf("Initial Atomspace size=%lu expect=%d\n", _as->get_size(), 7*idx+2);
    TSM_ASSERT("Initial unexpected atomspace size",
          ((unsigned)(7*idx+2)) == _as->get_size());
    /* Delete some atoms with non-empty incoming sets.
     * This should result in thier being removed from the atomspace,
     * but NOT fom the backend!
     */
    store->barrier();

    for (i=0; i<idx; i++)
        extract_links(i, _as);

    // +1 for (Predicate "*-IsKeyFlag-*"), +1 for *-open-*
    printf("Atomspace size=%lu expect=%d\n", _as->get_size(), 4*idx+2);
    TSM_ASSERT("Post-extract unexpected atomspace size", ((unsigned)(4*idx+2)) == _as->get_size());

    /* Verify that the atoms can still be fetched from storage. */
    for (i=0; i<idx; i++) {
        fetch_space(i, store);
        check_space(i, _as, "verify-fetch");
    }

    /* This time, kill them in the backend too.  */
    for (i=0; i<idx; i++)
        delete_links(i, store);

    printf("Post-remove size=%lu expect=%d\n", _as->get_size(), 4*idx+4);
    TSM_ASSERT("Post-remove unexpected atomspace size", ((unsigned)(4*idx+4)) == _as->get_size());

    /* Verify that the atoms are really gone from storage. */
    for (i=0; i<idx; i++) {
        fetch_space(i, store);
        check_remove(i, _as, "verify-gone");
    }

    printf("Post-referesh size=%lu expect=%d\n", _as->get_size(), 7*idx+4);
    TSM_ASSERT("Post-refetch unexpected atomspace size", ((unsigned)(7*idx+4)) == _as->get_size());

    /* Kill data for good */
    _as->clear();
    TSM_ASSERT("Non-empty atomspace", 0 == _as->get_size());

    /* Verify that the atoms are no longer being found. */
    for (i=0; i<idx; i++)
        check_empty(i, _as);

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

// ============================================================

void DeleteUTest::test_recurse(void)
{
    logger().debug("BEGIN TEST: %s", __FUNCTION__);

    Handle hsn = _as->add_node(COG_SIMPLE_STORAGE_NODE, "cog://localhost:16305");
    StorageNodePtr store = StorageNodeCast(hsn);
    store->setValue(_as->add_node(PREDICATE_NODE, "*-open-*"), createVoidValue());
    TS_ASSERT(store->connected())

    int idx = 0;
    add_to_space(idx++, _as, "AA-aa-wow ");
    add_to_space(idx++, _as, "BB-bb-wow ");
    add_to_space(idx++, _as, "CC-cc-wow ");
    add_to_space(idx++, _as, "DD-dd-wow ");
    add_to_space(idx++, _as, "EE-ee-wow ");

    /* Make sure UTF-8 works fine. */
    add_to_space(idx++, _as, "Попытка выбраться вызвала слабый стон ");
    add_to_space(idx++, _as, "はにがうりだそうであってるのかはち ");
    add_to_space(idx++, _as, "係拉丁字母");

    /* Verify that the atoms were added */
    int i;
    for (i=0; i<idx; i++)
        check_space(i, _as, "verify-add");

    /* Push all atoms out to the SQL DB */
    store->store_atomspace();

    /* Perform recursive extract of some nodes.
     * This should result in thier being removed from the atomspace,
     * but NOT fom the backend!
     */
    store->barrier();

    int zap = 4;
    for (i=0; i<zap; i++)
        extract_nodes(i, _as);

    // +1 for the *-IsKeyFlag-* predicate, +1 for *-open-* predicate
    size_t expsz = 7*(idx-zap) + 3*zap + 2;
    printf("Atomspace size=%lu expect=%lu\n",
         _as->get_size(), expsz);
    TSM_ASSERT("Unexpected atomspace size", expsz == _as->get_size());

    /* Verify that the atoms can still be fetched from storage. */
    for (i=0; i<idx; i++) {
        fetch_space(i, store);
        check_space(i, _as, "verify-fetch");
    }

    /* This time, kill them in the backend too.  */
    for (i=0; i<zap; i++)
        delete_nodes(i, store);

    printf("Post-remove size=%lu expect=%lu\n",
         _as->get_size(), expsz+2);
    TSM_ASSERT("Unexpected atomspace size", expsz+2 == _as->get_size());

    store->barrier();

    /* Verify that the atoms are really gone from storage. */
    for (i=0; i<idx; i++) {
        fetch_space(i, store);
    }

    printf("Post-refetch size=%lu expect=%d\n",
         _as->get_size(), 7*idx+4);
    TSM_ASSERT("Unexpected atomspace size",
         ((unsigned) 7*idx+4) == _as->get_size());

    /* Check that the removed atoms really got zapped. */
    for (i=0; i<zap; i++) {
        check_remove(i, _as, "verify-zapped");
    }

    /* However, others should still be there. */
    for (i=zap; i<idx; i++) {
        check_space(i, _as, "verify-zap");
    }

    /* Kill data for good */
    _as->clear();
    TSM_ASSERT("Non-empty atomspace", 0 == _as->get_size());

    /* Verify that the atoms are no longer being found. */
    for (i=0; i<idx; i++)
        check_empty(i, _as);

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

/* ============================= END OF FILE ================= */