OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace / tests / atomspace / COWSpaceUTest.cxxtest

169 lines · cxxtest ref:
/*
 * tests/atomspace/COWSpaceUTest.cxxtest
 *
 * Copyright (C) 2014,2015,2018 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/util/Logger.h>

#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 permsissions and Copy-On-Write(COW) support for multiple
// atomspaces. This is pursuant to github bug #1855 -- having
// permissioned access to values is a good thing to have.
//
class COWSpaceUTest :  public CxxTest::TestSuite
{
private:

	AtomSpacePtr base;
	AtomSpacePtr ovly;

public:
	COWSpaceUTest() {}

	void setUp() {
		base = createAtomSpace();
		ovly = createAtomSpace(base);
	}

	void tearDown() {
	}

	void testSimple()
	{
		logger().debug("BEGIN TEST: %s", __FUNCTION__);
		ValuePtr fv1(createFloatValue(std::vector<double>{0.1, 0.1}));
		ValuePtr fv2(createFloatValue(std::vector<double>{0.2, 0.2}));

		// Create two atoms in the base atomspace
		Handle h1 = base->add_node(CONCEPT_NODE, "A1 beef");
		Handle h1t = base->set_value(h1, test_key(), fv1);
		TS_ASSERT(h1 == h1t);

		Handle h2 = base->add_node(CONCEPT_NODE, "Grade A2");
		Handle h2t = base->set_value(h2, test_key(), fv2);
		TS_ASSERT(h2 == h2t);

		// Setting base to read-only should prevent insertion
		base->set_read_only();
		Handle h3 = base->add_node(CONCEPT_NODE, "3");
		printf("Read-only add: %p\n", h3.operator->());
		TS_ASSERT(h3 == nullptr);

		// Setting value in overlay should trigger COW
		ValuePtr fv3(createFloatValue(std::vector<double>{0.3, 0.3}));
		Handle h2o = ovly->set_value(h2, test_key(), fv3);
		TS_ASSERT(h2 != h2o);
		TS_ASSERT(h2o->getValue(test_key()) == fv3);
		TS_ASSERT(h2->getValue(test_key()) == fv2);

		Handle h2b = base->add_node(CONCEPT_NODE, "Grade A2");
		TS_ASSERT(h2 == h2b);
		TS_ASSERT(h2b->getValue(test_key()) == fv2);

		// Setting value a second time in overlay should NOT COW
		ValuePtr fv4(createFloatValue(std::vector<double>{0.4, 0.4}));
		Handle h2v = ovly->set_value(h2, test_key(), fv4);
		TS_ASSERT(h2 != h2v);
		TS_ASSERT(h2o == h2v);
		TS_ASSERT(h2o->getValue(test_key()) == fv4);
		TS_ASSERT(h2->getValue(test_key()) == fv2);

		Handle h2c = base->add_node(CONCEPT_NODE, "Grade A2");
		TS_ASSERT(h2 == h2c);
		TS_ASSERT(h2c->getValue(test_key()) == fv2);

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

	void testCOW()
	{
		logger().debug("BEGIN TEST: %s", __FUNCTION__);
		ValuePtr fv1(createFloatValue(std::vector<double>{0.1, 0.1}));
		ValuePtr fv2(createFloatValue(std::vector<double>{0.2, 0.2}));
		ValuePtr fv3(createFloatValue(std::vector<double>{0.3, 0.3}));
		ValuePtr fv4(createFloatValue(std::vector<double>{0.4, 0.4}));
		ValuePtr fv5(createFloatValue(std::vector<double>{0.5, 0.5}));

		// Mark the overlay as a COW space.
		ovly->set_copy_on_write();

		// Create two atoms in the base atomspace
		Handle h1 = base->add_node(CONCEPT_NODE, "A1 beef");
		Handle h1t = base->set_value(h1, test_key(), fv1);
		TS_ASSERT(h1 == h1t);

		Handle h2 = base->add_node(CONCEPT_NODE, "Grade A2");
		Handle h2t = base->set_value(h2, test_key(), fv2);
		TS_ASSERT(h2 == h2t);

		// Setting value in overlay should trigger COW
		Handle h2o = ovly->set_value(h2, test_key(), fv3);
		TS_ASSERT(h2 != h2o);
		TS_ASSERT(h2o->getValue(test_key()) == fv3);
		TS_ASSERT(h2->getValue(test_key()) == fv2);

		// No problem touching the base space.
		Handle h2b = base->add_node(CONCEPT_NODE, "Grade A2");
		TS_ASSERT(h2 == h2b);
		TS_ASSERT(h2b->getValue(test_key()) == fv2);

		// No problem altering the base space.
		Handle h4b = base->set_value(h2, test_key(), fv4);
		TS_ASSERT(h2 == h4b);
		TS_ASSERT(h4b->getValue(test_key()) == fv4);

		// The value in the overlay should not have been affected,
		// when the value in the base was changed.
		Handle h2u = ovly->add_node(CONCEPT_NODE, "Grade A2");
		TS_ASSERT(h2 != h2u);
		TS_ASSERT(h2o == h2u);
		TS_ASSERT(h2u->getValue(test_key()) == fv3);

		// Setting value a second time in overlay should NOT COW
		Handle h2v = ovly->set_value(h2, test_key(), fv5);
		TS_ASSERT(h2 != h2v);
		TS_ASSERT(h2o == h2v);
		TS_ASSERT(h2o->getValue(test_key()) == fv5);
		TS_ASSERT(h2->getValue(test_key()) == fv4);

		// Base should be OK.
		Handle h2c = base->add_node(CONCEPT_NODE, "Grade A2");
		TS_ASSERT(h2 == h2c);
		TS_ASSERT(h2c->getValue(test_key()) == fv4);

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