Note: not fixing all relevant warnings. Especially, the "-Woverloaded-virtual" of Clang defeats the whole purpose of generated generic interfaces. For example, our Variant type is instantiated with a list of types the variant can hold. Through metaprogramming, this instantiation generates also an embedded Visitor interface, which has virtual 'handle(TY)' functions for all the types in question The client now may implement, or even partially implement this Visitor, to retrieve specific data out of given Variant instance with unknown conent. To complain that some other virtual overload is now shaddowed is besides the point, so we might consider to disable this warning altogether
278 lines
9.6 KiB
C++
278 lines
9.6 KiB
C++
/*
|
|
GenNodeBasic(Test) - fundamental properties of a generic tree node element
|
|
|
|
Copyright (C) Lumiera.org
|
|
2015, Hermann Vosseler <Ichthyostega@web.de>
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of
|
|
the License, or (at your option) any later version.
|
|
|
|
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 General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
* *****************************************************/
|
|
|
|
|
|
#include "lib/test/run.hpp"
|
|
#include "lib/test/test-helper.hpp"
|
|
#include "lib/diff/gen-node.hpp"
|
|
#include "lib/diff/record.hpp"
|
|
#include "lib/time/timevalue.hpp"
|
|
#include "lib/util.hpp"
|
|
|
|
//#include <utility>
|
|
//#include <string>
|
|
//#include <vector>
|
|
#include <cmath>
|
|
|
|
using util::isnil;
|
|
using util::contains;
|
|
using util::isSameObject;
|
|
using lib::hash::LuidH;
|
|
using lib::time::FSecs;
|
|
using lib::time::Time;
|
|
using lib::time::TimeSpan;
|
|
//using std::string;
|
|
//using std::vector;
|
|
//using std::swap;
|
|
using std::fabs;
|
|
|
|
|
|
namespace lib {
|
|
namespace diff{
|
|
namespace test{
|
|
|
|
// using lumiera::error::LUMIERA_ERROR_LOGIC;
|
|
using error::LUMIERA_ERROR_WRONG_TYPE;
|
|
using error::LUMIERA_ERROR_BOTTOM_VALUE;
|
|
|
|
namespace {//Test fixture....
|
|
|
|
|
|
|
|
}//(End)Test fixture
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************//**
|
|
* @test Verify properties of a special collection type shaped for
|
|
* external representation of object-like data.
|
|
* - GenNode elements can be created "right away", picking up
|
|
* the given type, assumed that the payload is one of the
|
|
* supported basic types.
|
|
* - optionally, GenNode elements can be named
|
|
* - unnamed elements get a marker ID plus unique number extension
|
|
* - object-like elements can be represented by using a diff:Record<GenNode>
|
|
* as payload. Obviously, the resulting data structure type is recursive.
|
|
* - a shortcut is provided to simplify defining empty baseline objects
|
|
* - there is a special notation to create "id references", which can
|
|
* can be used to stand-in for an "object" (Record). This shortcut
|
|
* notation is relevant for the tree diff language -- used within
|
|
* Lumiera as "External Tree Description" of object networks.
|
|
*
|
|
* @see IndexTable
|
|
* @see DiffListApplication_test
|
|
*/
|
|
class GenNodeBasic_test : public Test
|
|
{
|
|
|
|
virtual void
|
|
run (Arg)
|
|
{
|
|
simpleUsage();
|
|
objectShortcut();
|
|
symbolReference();
|
|
sequenceIteration();
|
|
copy_and_move();
|
|
}
|
|
|
|
|
|
void
|
|
simpleUsage()
|
|
{
|
|
// can build from the supported value types
|
|
GenNode n1(42);
|
|
CHECK (42 == n1.data.get<int>());
|
|
CHECK (!n1.isNamed());
|
|
CHECK (contains (n1.idi.getSym(), "_CHILD_"));
|
|
CHECK (contains (name(n1), "_CHILD_"));
|
|
|
|
// can optionally be named
|
|
GenNode n2("π", 3.14159265358979323846264338328);
|
|
CHECK (fabs (3.14159265 - n2.data.get<double>()) < 1e-5 );
|
|
CHECK (n2.isNamed());
|
|
CHECK ("π" == n2.idi.getSym());
|
|
CHECK ("π" == name(n2));
|
|
|
|
// is a copyable value
|
|
GenNode n11(n1);
|
|
CHECK (n1 == n11);
|
|
CHECK (42 == n11.data.get<int>());
|
|
|
|
// is assignable with compatible payload value
|
|
n11.data = 24;
|
|
CHECK (n1 != n11);
|
|
CHECK (24 == n11.data.get<int>());
|
|
CHECK (42 == n1.data.get<int>());
|
|
|
|
// is assignable within the same kind of value
|
|
n1 = n11;
|
|
CHECK (n1 == n11);
|
|
|
|
// but assignment may not alter payload type
|
|
VERIFY_ERROR (WRONG_TYPE, n1 = n2 );
|
|
|
|
// can build recursive data structures
|
|
GenNode n3(Rec({GenNode("type", "spam"), GenNode("ham", "eggs")}));
|
|
CHECK ("spam" == n3.data.get<Rec>().getType());
|
|
CHECK ("eggs" == n3.data.get<Rec>().get("ham").data.get<string>());
|
|
CHECK ("ham" == n3.data.get<Rec>().get("ham").idi.getSym());
|
|
CHECK (n3.data.get<Rec>().get("ham").isNamed());
|
|
CHECK (!n3.isNamed());
|
|
}
|
|
|
|
|
|
void
|
|
objectShortcut()
|
|
{
|
|
auto o0 = MakeRec().genNode();
|
|
auto o1 = MakeRec().genNode("νόμος");
|
|
auto o2 = MakeRec().type("spam").genNode();
|
|
auto o3 = MakeRec().attrib("Ψ", int64_t(42), "π", 3.14159265358979323846264338328).genNode("λόγος");
|
|
|
|
CHECK (!o0.isNamed());
|
|
CHECK (isnil(o0.data.get<Rec>()));
|
|
CHECK ("NIL" == o0.data.get<Rec>().getType());
|
|
|
|
CHECK (o1.isNamed());
|
|
CHECK ("νόμος" == o1.idi.getSym());
|
|
CHECK (isnil(o1.data.get<Rec>()));
|
|
|
|
CHECK (!o2.isNamed());
|
|
CHECK ("spam" == o0.data.get<Rec>().getType());
|
|
CHECK (isnil(o2.data.get<Rec>()));
|
|
|
|
CHECK (o3.isNamed());
|
|
CHECK ("λόγος" == o3.idi.getSym());
|
|
CHECK ("NIL" == o3.data.get<Rec>().getType());
|
|
CHECK (GenNode("Ψ", int64_t(42)) == o3.data.get<Rec>().get("Ψ"));
|
|
CHECK (42L == o3.data.get<Rec>().get("Ψ").data.get<int64_t>());
|
|
CHECK (1e-7 > fabs (3.14159265 - o3.data.get<Rec>().get("π").data.get<double>()));
|
|
|
|
LuidH luid;
|
|
//Demonstration: object builder is based on the mutator mechanism for Records...
|
|
auto o4 = Rec::Mutator(o2.data.get<Rec>()) // ...use GenNode o2 as starting point
|
|
.appendChild(GenNode("τ", Time(1,2,3,4))) // a named node with Time value
|
|
.scope('*' // a char node
|
|
,"★" // a string node
|
|
,luid // a hash value (LUID)
|
|
,TimeSpan(Time::ZERO, FSecs(23,25)) // a time span
|
|
,MakeRec().type("ham").scope("eggs").genNode()) // a spam object
|
|
.genNode("baked beans"); // ---> finish into named node
|
|
|
|
CHECK (o4.isNamed());
|
|
CHECK ("baked beans" == o4.idi.getSym());
|
|
CHECK ("spam" == o4.data.get<Rec>().getType()); // this was "inherited" from o2
|
|
|
|
auto scope = o4.data.get<Rec>().scope();
|
|
CHECK (!isnil(scope));
|
|
CHECK (GenNode("τ", Time(1,2,3,4)) == *scope);
|
|
++scope;
|
|
CHECK (GenNode(char('*')) == *scope);
|
|
++scope;
|
|
CHECK ("★" == scope->data.get<string>());
|
|
++scope;
|
|
CHECK (luid == scope->data.get<LuidH>());
|
|
++scope;
|
|
CHECK (Time(920,0) == scope->data.get<TimeSpan>().end());
|
|
++scope;
|
|
auto spam = *scope;
|
|
CHECK (!++scope);
|
|
CHECK ("ham" == spam.data.get<Rec>().getType());
|
|
CHECK (spam.contains (GenNode("eggs")));
|
|
|
|
// but while o4 was based on o2,
|
|
// adding all the additional contents didn't mutate o2
|
|
CHECK (isnil(o2.data.get<Rec>()));
|
|
}
|
|
|
|
|
|
|
|
void
|
|
symbolReference()
|
|
{
|
|
GenNode ham = MakeRec().type("spam").attrib("τ", Time(23,42)).genNode("egg bacon sausage and spam");
|
|
|
|
GenNode::ID hamID(ham);
|
|
CHECK (hamID == ham.idi);
|
|
CHECK (hamID.getSym() == ham.idi.getSym());
|
|
CHECK (hamID.getHash() == ham.idi.getHash());
|
|
CHECK (contains (string(hamID), "spam")); // Lovely spam!
|
|
|
|
Ref ref1("egg bacon sausage and spam");
|
|
Ref ref2(ham);
|
|
|
|
CHECK (ref1.idi == ham.idi);
|
|
CHECK (ref2.idi == ham.idi);
|
|
|
|
// can stand-in for the original Record...
|
|
CHECK (isSameObject (ham, ref2.data.get<Rec>()));
|
|
VERIFY_ERROR (BOTTOM_VALUE, ref1.data.get<Rec>());
|
|
|
|
RecRef rr1 = ref1.data.get<RecRef>();
|
|
RecRef rr2 = ref2.data.get<RecRef>();
|
|
|
|
CHECK ( isnil(rr1));
|
|
CHECK (!isnil(rr2));
|
|
Rec& ham_ref = rr2;
|
|
CHECK (isSameObject(ham, ham_ref));
|
|
CHECK (isSameObject(&ham, rr2.get()));
|
|
|
|
// pre-defined special ref-tokens
|
|
CHECK ("_END_" == name(Ref::END));
|
|
CHECK ("_THIS_" == name(Ref::THIS));
|
|
CHECK ("_CHILD_" == name(Ref::CHILD));
|
|
CHECK ("_ATTRIBS_" == name(Ref::ATTRIBS));
|
|
|
|
CHECK (isnil (Ref::END.data.get<RecRef>()));
|
|
CHECK (isnil (Ref::THIS.data.get<RecRef>()));
|
|
CHECK (isnil (Ref::CHILD.data.get<RecRef>()));
|
|
CHECK (isnil (Ref::ATTRIBS.data.get<RecRef>()));
|
|
}
|
|
|
|
|
|
void
|
|
sequenceIteration()
|
|
{
|
|
UNIMPLEMENTED ("wtf");
|
|
}
|
|
|
|
|
|
void
|
|
copy_and_move()
|
|
{
|
|
UNIMPLEMENTED ("need to check that?");
|
|
}
|
|
};
|
|
|
|
|
|
/** Register this test class... */
|
|
LAUNCHER (GenNodeBasic_test, "unit common");
|
|
|
|
|
|
|
|
}}} // namespace lib::diff::test
|