/* ParameterDomain(Test) - document the handling of parameter type domain Copyright (C) 2026, 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 parameter-domain-test.cpp ** unit test \ref ParameterDomain_test */ #include "test/run.hpp" //#include "test/test-helper.hpp" #include "lib/par/domain.hpp" #include "lib/par/param-type.hpp" #include "test/diagnostic-output.hpp"////////////TODO //#include //#include //#include #include //using std::string; //using std::vector; //using std::swap; namespace lib { namespace par { namespace test{ // using lumiera::error::LUMIERA_ERROR_LOGIC; using std::floating_point; namespace {//Test helpers.... template constexpr NUM _MAX = std::numeric_limits::max(); template constexpr NUM _MIN = std::numeric_limits::lowest(); template constexpr FLO _EPSILON = std::numeric_limits::epsilon(); }//(End)Test helpers /**************************************************************************//** * @test cover properties of generic parameter containers. */ class ParameterDomain_test : public Test { virtual void run (Arg) { seedRand(); basics(); verify_valueLimits(); verify_valueAccess(); verify_valueSetting(); verify_opaqueAssign(); } void basics() { } /** @test base operation to bracket a given value * so that it fits within another target type's value domain. * @note properly handles the signed vs. unsigned mismatch, so that * conversion from signed to unsigned can not wrap. */ void verify_valueLimits() { // Generic check if the first (target) type can hold // only a limited part of the second (source) type's domain CHECK ((sub_domain == true)); CHECK ((sub_domain == true)); CHECK ((sub_domain == true)); CHECK ((sub_domain == false)); CHECK ((sub_domain == true)); CHECK ((sub_domain == false)); // Limit a value to within a target type's domain // (without actually converting into the target type) CHECK (preClamp (23) == 23); CHECK (preClamp (-55) == -55); CHECK (preClamp (23) == 23); CHECK (preClamp(-55) == 0); CHECK (preClamp (_MAX) == 32767); CHECK (preClamp (_MIN) == -32768); CHECK (preClamp (_MAX) == 65535); CHECK (preClamp (_MIN) == 0 ); // the return value has the same type as the argument... CHECK (preClamp (_MAX) == _MAX); CHECK (preClamp (_MIN) == double(_MIN) ); // Warning: the following is not standard C++, yet supported on GCC / CLang / libstdc++ static_assert (_MAX < _MAX); CHECK (float(_MAX) == "inf"_expect ); CHECK ((unsigned __int128)_MAX == "340282346638528859811704183484516925440"_expect ); CHECK (preClamp (_MAX) == (unsigned __int128)_MAX); // Note: a trigger at 0.5+ε is built into the clamp towards bool CHECK (preClamp (0.5) == 0 ); CHECK (preClamp (0.5+_EPSILON) == 1 ); CHECK (preClamp (_MIN) == 0 ); // Systematic coverage of signed ⟷ unsigned CHECK (preClamp (_MAX) == 127); CHECK (preClamp ( int16_t{0}) == 0); CHECK (preClamp (_MIN) == -128); CHECK (preClamp (_MAX) == 127); CHECK (preClamp (_MIN) == 0); CHECK (preClamp (_MAX) == 255); CHECK (preClamp ( int16_t{0}) == 0); CHECK (preClamp (_MIN) == 0); CHECK (preClamp (_MAX) == 127); CHECK (preClamp ( int8_t{0}) == 0); CHECK (preClamp (_MIN) == -128); CHECK (preClamp (_MAX) == 255); CHECK (preClamp (_MIN) == 0); CHECK (preClamp (_MAX) == 127); CHECK (preClamp ( int8_t{0}) == 0); CHECK (preClamp (_MIN) == 0); CHECK (preClamp (_MAX) == 127); CHECK (preClamp ( int8_t{0}) == 0); CHECK (preClamp (_MIN) == -128); CHECK (preClamp (_MAX) == 127); CHECK (preClamp (_MIN) == 0); CHECK (preClamp (_MAX) == 127); CHECK (preClamp ( int8_t{0}) == 0); CHECK (preClamp (_MIN) == 0); } template using FullDomain = BaseDomain; /** @test base function to access the contents of an opaque buffer * with known type, and convert into another target type. * - the base interface #Domain represents an opaque type * - it exposes #TypeHandler interfaces for a fixed selection * of common elementary #BaseTypes * - here we use a `BaseDomain` as actual implementation, * yet knowledge regarding the type `int` is erased and any * access is based solely on a `Domain&` * - an opaque source ValBuf is passed explicitly * @remark this covers the value access capability of a * type handler interface, working on external storage. */ void verify_valueAccess() { FullDomain domInt; CHECK (sizeof(FullDomain) <= sizeof(void*)); // only the VTable // use the generic interface for access... Domain& dom{domInt}; int val = 1 + rani (1000); ValBuff& src = asValBuff (val); uint target1{0}; CHECK (not target1); dom.extractAs (target1, src); CHECK (target1 == uint(val)); float target2{0}; dom.extractAs (target2, src); CHECK (target2 == float(val)); // value clamped to target domain... val = _MIN; uint64_t target3{55}; dom.extractAs (target3, src); CHECK (target3 == _MIN); } /** @test base function to assign a directly given data value * into an opaque buffer with known (yet different) type. * @remark since the type handler interface covers both reading * and writing, here the roles are reversed here, so that the * target of the assignment is the opaque buffer, whose actual * type remains hidden behind the #Domain interface. */ void verify_valueSetting() { FullDomain domInt; Domain& dom{domInt}; int val_in_buff = 0; ValBuff& target = asValBuff (val_in_buff); uint src1 = 1 + rani (1000); dom.conform (target, src1); CHECK (val_in_buff == int(src1)); double src2 = ranRange (1,100); CHECK (1 <= src2 and src2 < 100); dom.conform (target, src2); CHECK (val_in_buff == int(floor (src2))); // value clamped to target domain... auto mini = _MIN; dom.conform (target, mini); CHECK (val_in_buff == _MIN); } /** @test value transfer between opaque buffers with double-dispatch. * - use several typed values, yet access these only marked as `ValBuff` * - the call must be passed through _two virtual functions_, in order * to re-gain the fully typed context; this is the »visitor pattern« * @remark the API might seem bewildering; this is due to passing both source * and target as reference to _some value buffer_, since this API describes * only a data type disposition, while the actual data storage is defined * as derived parameter data type. */ void verify_opaqueAssign() { double double_val = ranRange (2,100); int64_t i64_val{0}; bool boo{false}; FullDomain ddom; FullDomain idom; FullDomain bdom; Domain& d1{ddom}; Domain& d3{bdom}; CHECK (i64_val == 0); d1.transferTo (asValBuff(double_val), idom, asValBuff(i64_val)); CHECK (i64_val == int64_t(floor (double_val))); CHECK (not boo); d1.transferTo (asValBuff(double_val), bdom, asValBuff(boo)); CHECK (boo); CHECK (double_val != 1.0); d3.transferTo (asValBuff(boo), ddom, asValBuff(double_val)); CHECK (double_val == 1.0); } }; /** Register this test class... */ LAUNCHER (ParameterDomain_test, "unit lib"); }}} // namespace lib::par::test