/*
* tests/atomspace/MultiSpaceUTest.cxxtest
*
* Copyright (C) 2014,2015 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/atoms/base/Node.h>
#include <opencog/atoms/base/Link.h>
#include <opencog/atomspace/AtomSpace.h>
#include <opencog/atoms/value/FloatValue.h>
#include <cxxtest/TestSuite.h>
using namespace opencog;
// Test key for storing values
static Handle test_key()
{
static Handle key(createNode(PREDICATE_NODE, "*-test-key-*"));
return key;
}
// Test support for multiple atomspaces.
// This is pursuant to github bug #523 -- several users want this
// ability.
// Tests for removal of atoms in a multi-atomspace usage case is in
// the RemoveUTest.cxxtest file.
//
class MultiSpaceUTest : public CxxTest::TestSuite
{
private:
AtomSpacePtr asp1;
AtomSpacePtr asp2;
AtomSpacePtr asp3;
AtomSpace& as1;
AtomSpace& as2;
AtomSpace& as3;
public:
MultiSpaceUTest() :
asp1(createAtomSpace()),
asp2(createAtomSpace()),
asp3(createAtomSpace()),
as1(*asp1.get()),
as2(*asp2.get()),
as3(*asp3.get())
{}
void setUp() {}
void tearDown() {}
// Simple test of naive multiple atomspaces. Create three
// distinct atomspaces, make sure they behave like atomspaces
// should.
void testSimple()
{
// Create three float values
ValuePtr fv1(createFloatValue(std::vector<double>{0.5, 1.0}));
ValuePtr fv2(createFloatValue(std::vector<double>{0.5, 0.5}));
ValuePtr fv3(createFloatValue(std::vector<double>{0.5, 0.333}));
// Create three atoms in three different atomspaces
Handle h1 = as1.add_node(CONCEPT_NODE, "1");
h1->setValue(test_key(), fv1);
Handle h2 = as2.add_node(NUMBER_NODE, "2");
h2->setValue(test_key(), fv2);
Handle h3 = as3.add_node(VARIABLE_NODE, "3");
h3->setValue(test_key(), fv3);
// Fetch three atoms in three different atomspaces
Handle hn1 = as1.add_node(CONCEPT_NODE, "1");
Handle hn2 = as2.add_node(NUMBER_NODE, "2");
Handle hn3 = as3.add_node(VARIABLE_NODE, "3");
// They should match the old handles
TS_ASSERT(h1 == hn1);
TS_ASSERT(h2 == hn2);
TS_ASSERT(h3 == hn3);
// ... and they should refer to the same atoms.
TS_ASSERT(hn1->get_type() == h1->get_type());
TS_ASSERT(hn2->get_type() == h2->get_type());
TS_ASSERT(hn3->get_type() == h3->get_type());
// The value *pointers* should be identical, as they
// should point to the same exact value instance.
TS_ASSERT(hn1->getValue(test_key()) == h1->getValue(test_key()));
TS_ASSERT(hn2->getValue(test_key()) == h2->getValue(test_key()));
TS_ASSERT(hn3->getValue(test_key()) == h3->getValue(test_key()));
}
// Test multiple independent atomspaces. Make sure they do not
// collide with one-another in atom insertion, deletion.
void testCopy()
{
// Create three float values
ValuePtr fv1(createFloatValue(std::vector<double>{0.4, 1.0}));
ValuePtr fv2(createFloatValue(std::vector<double>{0.4, 0.5}));
ValuePtr fv3(createFloatValue(std::vector<double>{0.4, 0.333}));
// Create three nodes
Handle n1(createNode(CONCEPT_NODE, "uni-1"));
n1->setValue(test_key(), fv1);
Handle n2(createNode(NUMBER_NODE, "2.2222"));
n2->setValue(test_key(), fv2);
Handle n3(createNode(VARIABLE_NODE, "uni-3"));
n3->setValue(test_key(), fv3);
// Create three links holding these three
ValuePtr fv(createFloatValue(std::vector<double>{0.33, 0.333}));
// Create three copies of this link in three atomspaces
Handle h1 = as1.add_link(LIST_LINK, n1, n2, n3);
h1->setValue(test_key(), fv);
Handle h2 = as2.add_atom(h1);
Handle h3 = as3.add_atom(h2);
// The values themselves should be equal, as values.
TS_ASSERT(*h1->getValue(test_key()) == *fv);
TS_ASSERT(*h2->getValue(test_key()) == *fv);
TS_ASSERT(*h3->getValue(test_key()) == *fv);
// Fetch three atoms in three different atomspaces
Handle h1n1 = as1.get_node(CONCEPT_NODE, "uni-1");
Handle h1n2 = as1.get_node(NUMBER_NODE, "2.2222");
Handle h1n3 = as1.get_node(VARIABLE_NODE, "uni-3");
Handle h2n1 = as2.get_node(CONCEPT_NODE, "uni-1");
Handle h2n2 = as2.get_node(NUMBER_NODE, "2.2222");
Handle h2n3 = as2.get_node(VARIABLE_NODE, "uni-3");
Handle h3n1 = as3.get_node(CONCEPT_NODE, "uni-1");
Handle h3n2 = as3.get_node(NUMBER_NODE, "2.2222");
Handle h3n3 = as3.get_node(VARIABLE_NODE, "uni-3");
// They should NOT match one-another
// They should have become copies.
TS_ASSERT(h1n1 != h2n1);
TS_ASSERT(h1n2 != h2n2);
TS_ASSERT(h1n3 != h2n3);
TS_ASSERT(h1n1 != h3n1);
TS_ASSERT(h1n2 != h3n2);
TS_ASSERT(h1n3 != h3n3);
TS_ASSERT(h2n1 != h3n1);
TS_ASSERT(h2n2 != h3n2);
TS_ASSERT(h2n3 != h3n3);
// The values themselves should be equal, as values.
TS_ASSERT(*h1n1->getValue(test_key()) == *fv1);
TS_ASSERT(*h2n1->getValue(test_key()) == *fv1);
TS_ASSERT(*h3n1->getValue(test_key()) == *fv1);
TS_ASSERT(*h1n2->getValue(test_key()) == *fv2);
TS_ASSERT(*h2n2->getValue(test_key()) == *fv2);
TS_ASSERT(*h3n2->getValue(test_key()) == *fv2);
TS_ASSERT(*h1n3->getValue(test_key()) == *fv3);
TS_ASSERT(*h2n3->getValue(test_key()) == *fv3);
TS_ASSERT(*h3n3->getValue(test_key()) == *fv3);
}
// Test copying back and forth.
void testCopyBack()
{
// Create a node, copy it back and forth.
Handle n1 = as1.add_node(CONCEPT_NODE, "backer");
Handle n2 = as2.add_atom(n1);
Handle n1copy = as1.add_atom(n2);
Handle n2copy = as2.add_atom(n1copy);
TS_ASSERT(n1 == n1copy);
TS_ASSERT(n2 == n2copy);
// Create a link, copy it back and forth.
Handle h1 = as1.add_link(LIST_LINK, n1);
Handle h2 = as2.add_atom(h1);
Handle h1copy = as1.add_atom(h2);
Handle h2copy = as2.add_atom(h1copy);
TS_ASSERT(h1 == h1copy);
TS_ASSERT(h2 == h2copy);
Handle h3 = as3.add_atom(h1);
Handle h3copy = as3.add_atom(h2);
TS_ASSERT(h3 == h3copy);
// Do it again, this time with unordered links.
Handle ma = as1.add_node(CONCEPT_NODE, "hacker");
Handle mb = as1.add_node(CONCEPT_NODE, "cracker");
Handle mc = as1.add_node(CONCEPT_NODE, "jacker");
h1 = as1.add_link(UNORDERED_LINK, n1, mc, mb, n1, mc, ma);
h2 = as2.add_atom(h1);
h1copy = as1.add_atom(h2);
h2copy = as2.add_atom(h1copy);
TS_ASSERT(h1 == h1copy);
TS_ASSERT(h2 == h2copy);
h3 = as3.add_atom(h1);
h3copy = as3.add_atom(h2);
TS_ASSERT(h3 == h3copy);
}
// Like testCopyBack(), except that it explicitly calls the getAtom
// method. Because the atoms exist, the get should return them.
void testGet()
{
// Create a node, copy it back and forth.
Handle n1 = as1.add_node(CONCEPT_NODE, "Ziggy Stardust");
Handle n2 = as2.add_atom(n1);
Handle n1copy = as1.get_atom(n2);
Handle n2copy = as2.get_atom(n1copy);
TS_ASSERT(n1 == n1copy);
TS_ASSERT(n2 == n2copy);
// Create a link, copy it back and forth.
Handle h1 = as1.add_link(LIST_LINK, n1);
Handle h2 = as2.add_atom(h1);
Handle h1copy = as1.get_atom(h2);
Handle h2copy = as2.get_atom(h1copy);
TS_ASSERT(h1 == h1copy);
TS_ASSERT(h2 == h2copy);
Handle h3 = as3.get_atom(h1);
Handle h3copy = as3.get_atom(h2);
TS_ASSERT(h3 == h3copy);
// Do it again, this time with unordered links.
Handle ma = as1.add_node(CONCEPT_NODE, "ziggy");
Handle mb = as1.add_node(CONCEPT_NODE, "weird");
Handle mc = as1.add_node(CONCEPT_NODE, "gilly");
h1 = as1.add_link(UNORDERED_LINK, n1, mc, mb, n1, mc, ma);
h2 = as2.add_atom(h1);
h1copy = as1.get_atom(h2);
h2copy = as2.get_atom(h1copy);
TS_ASSERT(h1 == h1copy);
TS_ASSERT(h2 == h2copy);
h3 = as3.add_atom(h1);
h3copy = as3.get_atom(h2);
TS_ASSERT(h3 == h3copy);
}
// Like testCopyBack() and testGet(), except that one of the
// atomspaces is an expansion. This exhibits a new bug,
// github bug report #1530.
void testExpand()
{
AtomSpacePtr ex(createAtomSpace(asp1));
AtomSpace& ex1(*ex);
// Create a node, copy it back and forth.
Handle n1 = ex1.add_node(CONCEPT_NODE, "Spiders from Mars");
Handle n2 = as2.add_atom(n1);
Handle n1copy = ex1.get_atom(n2);
Handle n2copy = as2.get_atom(n1copy);
TS_ASSERT(n1 == n1copy);
TS_ASSERT(n2 == n2copy);
// Create a link, copy it back and forth.
Handle h1 = ex1.add_link(LIST_LINK, n1);
Handle h2 = as2.add_atom(h1);
Handle h1copy = ex1.get_atom(h2);
Handle h2copy = as2.get_atom(h1copy);
TS_ASSERT(h1 == h1copy);
TS_ASSERT(h2 == h2copy);
Handle h3 = as3.get_atom(h1);
Handle h3copy = as3.get_atom(h2);
TS_ASSERT(h3 == h3copy);
// Do it again, this time with unordered links.
Handle ma = ex1.add_node(CONCEPT_NODE, "wham");
Handle mb = ex1.add_node(CONCEPT_NODE, "bam");
Handle mc = ex1.add_node(CONCEPT_NODE, "thankyou, mam");
h1 = ex1.add_link(UNORDERED_LINK, n1, mc, mb, n1, mc, ma);
h2 = as2.add_atom(h1);
h1copy = ex1.get_atom(h2);
h2copy = as2.get_atom(h1copy);
TS_ASSERT(h1 == h1copy);
TS_ASSERT(h2 == h2copy);
h3 = as3.add_atom(h1);
h3copy = as3.get_atom(h2);
TS_ASSERT(h3 == h3copy);
}
// Test multiple nested (hierarchical) atomspaces. They should
// work as expected... The below is a fairly naive test, it does
// not test any kind of complicated semantics. Its minimal.
void testNest()
{
AtomSpacePtr nas1(createAtomSpace());
AtomSpacePtr nas2(createAtomSpace(nas1));
AtomSpacePtr nas3(createAtomSpace(nas2));
// Create three float values
ValuePtr fv1(createFloatValue(std::vector<double>{0.4, 1.0}));
ValuePtr fv2(createFloatValue(std::vector<double>{0.4, 0.5}));
ValuePtr fv3(createFloatValue(std::vector<double>{0.4, 0.333}));
// Create three nodes in the base atomspace
Handle hnd1 = nas1->add_node(CONCEPT_NODE, "nest-1");
hnd1->setValue(test_key(), fv1);
Handle hnd2 = nas1->add_node(NUMBER_NODE, "2.34");
hnd2->setValue(test_key(), fv2);
Handle hnd3 = nas1->add_node(VARIABLE_NODE, "nest-3");
hnd3->setValue(test_key(), fv3);
// Create three links holding these three
ValuePtr fv(createFloatValue(std::vector<double>{0.33, 0.333}));
// Create a link in the middle atomspace
Handle h2 = nas2->add_link(LIST_LINK, hnd1, hnd2, hnd3);
h2->setValue(test_key(), fv);
// Try to create a link in the grandchild atomspace
Handle h3 = nas3->add_link(LIST_LINK, hnd1, hnd2, hnd3);
h3->setValue(test_key(), fv);
// The middle and grand-child links should be one and the same.
TS_ASSERT(h2 == h3);
// Fetch three atoms in three different atomspaces
Handle h1n1 = nas1->add_node(CONCEPT_NODE, "nest-1");
Handle h1n2 = nas1->add_node(NUMBER_NODE, "2.34");
Handle h1n3 = nas1->add_node(VARIABLE_NODE, "nest-3");
Handle h2n1 = nas2->add_node(CONCEPT_NODE, "nest-1");
Handle h2n2 = nas2->add_node(NUMBER_NODE, "2.34");
Handle h2n3 = nas2->add_node(VARIABLE_NODE, "nest-3");
Handle h3n1 = nas3->add_node(CONCEPT_NODE, "nest-1");
Handle h3n2 = nas3->add_node(NUMBER_NODE, "2.34");
Handle h3n3 = nas3->add_node(VARIABLE_NODE, "nest-3");
// They should be identical
// They should NOT be copies.
TS_ASSERT(hnd1 == h1n1);
TS_ASSERT(hnd2 == h1n2);
TS_ASSERT(hnd3 == h1n3);
TS_ASSERT(hnd1 == h2n1);
TS_ASSERT(hnd2 == h2n2);
TS_ASSERT(hnd3 == h2n3);
TS_ASSERT(hnd1 == h3n1);
TS_ASSERT(hnd2 == h3n2);
TS_ASSERT(hnd3 == h3n3);
// The value *pointers* should be identical, of course,
// since they are the very same atoms.
TS_ASSERT(hnd1->getValue(test_key()) == h1n1->getValue(test_key()));
TS_ASSERT(hnd2->getValue(test_key()) == h1n2->getValue(test_key()));
TS_ASSERT(hnd3->getValue(test_key()) == h1n3->getValue(test_key()));
TS_ASSERT(hnd1->getValue(test_key()) == h2n1->getValue(test_key()));
TS_ASSERT(hnd2->getValue(test_key()) == h2n2->getValue(test_key()));
TS_ASSERT(hnd3->getValue(test_key()) == h2n3->getValue(test_key()));
TS_ASSERT(hnd1->getValue(test_key()) == h3n1->getValue(test_key()));
TS_ASSERT(hnd2->getValue(test_key()) == h3n2->getValue(test_key()));
TS_ASSERT(hnd3->getValue(test_key()) == h3n3->getValue(test_key()));
}
// This tests bug report #9
void testCrash()
{
AtomSpacePtr ex1 = createAtomSpace(asp1);
// Create mixed nodes.
Handle n1 = as1.add_node(CONCEPT_NODE, "aaa");
Handle n2 = ex1->add_node(CONCEPT_NODE, "bbb");
// Create a link
Handle h1 = ex1->add_atom(createLink(LIST_LINK, n1, n2));
std::vector<Handle> handle_set;
as1.get_handles_by_type(handle_set, ATOM, false);
// cogserver crashes at this point; no crash == pass
AtomSpacePtr ex2 = createAtomSpace(asp2);
// Create mixed nodes.
n2 = ex2->add_node(CONCEPT_NODE, "bbb");
// Create a link
h1 = ex2->add_atom(createLink(LIST_LINK, n1, n2));
handle_set.clear();
as1.get_handles_by_type(handle_set, ATOM, false);
// cogserver crashes at this point; no crash == pass
}
// This tests bug report #97
void testGetType()
{
AtomSpacePtr as = createAtomSpace();
AtomSpacePtr ex = createAtomSpace(as);
// Add nodes of same typ.
as->add_node(CONCEPT_NODE, "aaa");
ex->add_node(CONCEPT_NODE, "bbb");
HandleSeq handle_set;
ex->get_handles_by_type(handle_set, CONCEPT_NODE);
TS_ASSERT(handle_set.size() == 2);
ex->clear();
TS_ASSERT(as->get_size() == 1);
TS_ASSERT(ex->get_size() == 1);
handle_set.clear();
ex->get_handles_by_type(handle_set, CONCEPT_NODE);
TS_ASSERT(handle_set.size() == 1);
}
// This tests bug report #221
void testMoreSpace()
{
AtomSpacePtr as(createAtomSpace());
AtomSpacePtr ex(createAtomSpace(as));
AtomSpacePtr ns(createAtomSpace());
Handle haa = as->add_node(CONCEPT_NODE, "PLN");
Handle hab = as->add_node(CONCEPT_NODE, "URE");
Handle hea = ex->add_link(LIST_LINK, haa, hab);
Handle heb = ex->add_node(CONCEPT_NODE, "VAR");
Handle hec = ex->add_link(IMPLICATION_LINK, hea, heb);
as->add_link(LIST_LINK, haa, hab);
Handle hna = ns->add_atom(haa);
Handle hnb = ns->add_atom(hec);
Handle haa_copy = ex->get_atom(hna);
Handle hec_copy = ex->get_atom(hnb);
TS_ASSERT(haa_copy == haa);
TS_ASSERT(hec_copy == hec);
}
};