/* GenericRecord(Test) - introspective representation of object-like data Copyright (C) 2015, Hermann Vosseler   **Lumiera** 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. See the file COPYING for further details. * *****************************************************************/ /** @file generic-record-test.cpp ** unit test \ref GenericRecord_test */ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" #include "lib/format-cout.hpp" #include "lib/format-util.hpp" #include "lib/diff/record.hpp" #include "lib/itertools.hpp" #include #include using std::string; using util::isSameObject; using util::isnil; using util::join; using std::vector; using std::swap; namespace lib { namespace diff{ namespace test{ using LERR_(INVALID); using LERR_(INDEX_BOUNDS); using LERR_(BOTTOM_VALUE); namespace {//Test fixture.... using Seq = vector; using RecS = Record; template inline Seq contents (IT const& it) { Seq collected; append_all (it, collected); return collected; } inline Seq contents (RecS const& rec_of_strings) { return contents (rec_of_strings.begin()); } template inline Seq strings (std::initializer_list const& con) { Seq collected; for (auto elm : con) collected.push_back(elm); return collected; } }//(End)Test fixture /*************************************************************************************//** * @test Verify properties of a special collection type meant for external representation * of object-like data, especially for symbolic representation in diff messages. * - there is a type meta attribute * - a Record can have attributes (by key) and contents (ordered list of values) * - various kinds of iterators are provided * - besides the regular constructor, which explicitly takes a type, a collection * of attributes, and a collection of contents, there is a convenience constructor * especially for literal notation and data definition. This one figures out the * break between attributes and contents automatically; a type meta attribute * is recognised and the first element without a given key or ID ends the * attributes and starts the content scope * - Record elements are conceived as values and equality is defined in terms * of their contents, including the order (no normalisation, no sorting) * - they are \em immutable after construction. But we provide a Mutator * for remoulding a given element, enabling object builder notation. * - a reference wrapper for handling of large structures is provided. * @remarks this test uses the specialisation \c Record solely, to cover the * basic properties and behaviour, while leaving out the complexities of specific * payload data types. For the actual use case, the symbolic description of * data structure differences, we use a specific "value" within Record, * the diff::GenNode, which is a limited typesafe Variant element, and in * turn allows Record as embedded payload. Effectively this creates * a "recursive data type", which is key to typesafe functional processing of * unlimited data structures. The design of diff::Record only makes sense with * this use case in mind; most notably, we have the keys (attribute names) * embedded within the value payload, which turns attributes into just another * content scope with special access operations. This also explains, why we * do not normalise the content in any way; content is meant to reflect * other data structures, which are normalised and maintained by their owner. * * @see GenNode_test * @see tree-diff.cpp */ class GenericRecord_test : public Test { virtual void run (Arg) { simpleUsage(); verifyCreation(); verifyMutations(); copy_and_move(); equality(); wrapRef(); } void simpleUsage() { RecS enterprise("starship" , strings ({"Name = USS Enterprise" ,"Registry = NCC-1701-D" ,"Class = Galaxy" ,"Owner = United Federation of Planets" ,"Operator= Starfleet" ,"built=2363" }) , strings ({"Picard", "Riker", "Data", "Troi", "Worf", "Crusher", "La Forge"}) ); CHECK (enterprise.getType() == "starship"); CHECK (enterprise.get("Registry") == "NCC-1701-D"); CHECK (enterprise.child(0) == "Picard"); CHECK (enterprise.child(2) == "Data"); CHECK (enterprise.hasAttribute("Owner")); CHECK (!enterprise.hasAttribute("owner")); CHECK (!enterprise.hasAttribute("Owner ")); // no normalisation CHECK (enterprise.contains("Data")); CHECK (!enterprise.contains("Woof")); // it is /Worf/, madam CHECK (util::contains (enterprise, "Worf")); VERIFY_ERROR (INVALID, enterprise.get("warp10")); VERIFY_ERROR (INDEX_BOUNDS, enterprise.child(12)); cout << "enterprise = " << enterprise <" << join (enterprise.keys(), "<->")<" << join (enterprise.vals(), "<->")<" << join (enterprise.scope()," | ")< empty; CHECK (bool(empty) == false); CHECK (nullptr == empty.get()); VERIFY_ERROR (BOTTOM_VALUE, empty.operator RecS&() ); RecordRef ref(oo); CHECK (ref); CHECK (ref.get() == &oo); RecS& oor = ref; CHECK ("🌰" == oor.getType()); CHECK (oor.get("♄") == "saturn"); // are copyable but not reassignable RecordRef r2 = ref; CHECK (r2); CHECK (r2.get() == ref.get()); CHECK (!isSameObject (r2, ref)); // but references are move-assignable empty = std::move(r2); CHECK (empty); CHECK (!r2); CHECK (nullptr == r2.get()); } }; /** Register this test class... */ LAUNCHER (GenericRecord_test, "unit common"); }}} // namespace lib::diff::test