2023-05-03 15:27:46 +02:00
|
|
|
|
/*
|
|
|
|
|
|
SplitSplice(Test) - verify interval splicing
|
|
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
Copyright (C)
|
|
|
|
|
|
2023, Hermann Vosseler <Ichthyostega@web.de>
|
2023-05-03 15:27:46 +02:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
**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.
|
2023-05-03 15:27:46 +02:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
* *****************************************************************/
|
2023-05-03 15:27:46 +02:00
|
|
|
|
|
|
|
|
|
|
/** @file split-splice-test.cpp
|
|
|
|
|
|
** unit test \ref SplitSplice_test
|
2023-05-04 14:43:40 +02:00
|
|
|
|
** to verify proper working of the »SplitSplice« algorithm.
|
|
|
|
|
|
** This is a generic setup to modify a segmentation (partitioning)
|
|
|
|
|
|
** of an ordered axis; the axis is represented as a collection of _segments,_
|
|
|
|
|
|
** which are assumed to be ordered and seamless, with the start point inclusive
|
|
|
|
|
|
** and the end point exclusive (thus the start of the next segment is identical
|
|
|
|
|
|
** with the end point of the current segment).
|
|
|
|
|
|
**
|
|
|
|
|
|
** This test uses the natural number axis between -100 ... +100
|
|
|
|
|
|
** and establishes a binding for the generic algorithm with suitably rigged
|
|
|
|
|
|
** test data, to verify the algorithm properly inserts a new segment under all
|
|
|
|
|
|
** conceivable circumstances, since there are many possibilities of arrangement
|
|
|
|
|
|
** for two ordered segments of arbitrary length.
|
2023-05-03 15:27:46 +02:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "lib/test/run.hpp"
|
2023-05-04 00:48:29 +02:00
|
|
|
|
#include "lib/test/test-helper.hpp"
|
2023-05-03 15:27:46 +02:00
|
|
|
|
#include "lib/format-cout.hpp"
|
|
|
|
|
|
#include "lib/format-util.hpp"
|
|
|
|
|
|
#include "lib/format-string.hpp"
|
|
|
|
|
|
#include "lib/split-splice.hpp"
|
|
|
|
|
|
#include "lib/nocopy.hpp"
|
|
|
|
|
|
#include "lib/util.hpp"
|
|
|
|
|
|
|
2023-08-08 00:35:26 +02:00
|
|
|
|
#include <optional>
|
2023-05-03 15:27:46 +02:00
|
|
|
|
#include <utility>
|
|
|
|
|
|
#include <string>
|
|
|
|
|
|
#include <list>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace lib {
|
|
|
|
|
|
namespace test {
|
|
|
|
|
|
|
|
|
|
|
|
using util::_Fmt;
|
|
|
|
|
|
using util::isnil;
|
2024-11-14 22:10:43 +01:00
|
|
|
|
using util::getAdr;
|
2023-05-05 01:34:41 +02:00
|
|
|
|
using util::isSameObject;
|
2023-05-03 15:27:46 +02:00
|
|
|
|
using std::string;
|
|
|
|
|
|
using std::move;
|
2023-05-04 00:48:29 +02:00
|
|
|
|
|
2023-05-03 15:27:46 +02:00
|
|
|
|
namespace {// Test Fixture....
|
|
|
|
|
|
|
2023-05-04 00:48:29 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* Test Dummy: a "segment" representing an integer interval.
|
|
|
|
|
|
* Memory management can be tracked since each instance gets a
|
|
|
|
|
|
* distinct ID number. Moreover, a Seg can be marked as "empty",
|
|
|
|
|
|
* which is visible on the `string` conversion
|
|
|
|
|
|
*/
|
2023-05-03 15:27:46 +02:00
|
|
|
|
struct Seg
|
|
|
|
|
|
: util::MoveOnly
|
|
|
|
|
|
{
|
|
|
|
|
|
int start;
|
|
|
|
|
|
int after;
|
|
|
|
|
|
bool empty;
|
|
|
|
|
|
|
2023-05-04 02:07:38 +02:00
|
|
|
|
~Seg()
|
|
|
|
|
|
{
|
|
|
|
|
|
check -= id;
|
|
|
|
|
|
if (id) --cnt;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-05-03 15:27:46 +02:00
|
|
|
|
Seg (int s, int a, bool nil=false)
|
|
|
|
|
|
: start{s}
|
|
|
|
|
|
, after{a}
|
|
|
|
|
|
, empty{nil}
|
|
|
|
|
|
, id{++idGen}
|
|
|
|
|
|
{
|
2023-05-04 00:48:29 +02:00
|
|
|
|
++cnt;
|
2023-05-03 15:27:46 +02:00
|
|
|
|
check += id;
|
|
|
|
|
|
}
|
2023-05-04 00:48:29 +02:00
|
|
|
|
|
2023-05-04 02:07:38 +02:00
|
|
|
|
/** create a clone, but modify bounds */
|
|
|
|
|
|
Seg (Seg const& ref, int s, int a)
|
|
|
|
|
|
: start{s}
|
|
|
|
|
|
, after{a}
|
|
|
|
|
|
, empty{ref.empty}
|
|
|
|
|
|
, id{ref.id}
|
2023-05-03 15:27:46 +02:00
|
|
|
|
{
|
2023-05-04 02:07:38 +02:00
|
|
|
|
++cnt;
|
|
|
|
|
|
check += id;
|
2023-05-03 15:27:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-05-04 02:07:38 +02:00
|
|
|
|
/** move-init: causes source-ref to be invalidated */
|
2023-05-03 15:27:46 +02:00
|
|
|
|
Seg (Seg&& rr)
|
|
|
|
|
|
: start{rr.start}
|
|
|
|
|
|
, after{rr.after}
|
|
|
|
|
|
, empty{rr.empty}
|
|
|
|
|
|
, id{0}
|
|
|
|
|
|
{
|
|
|
|
|
|
std::swap (id, rr.id);
|
|
|
|
|
|
}//transfer identity
|
|
|
|
|
|
|
|
|
|
|
|
operator string() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return _Fmt{"[%i%s%i["}
|
|
|
|
|
|
% start
|
|
|
|
|
|
% (empty? "~":"_")
|
|
|
|
|
|
% after
|
|
|
|
|
|
;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-- Diagnostics --
|
|
|
|
|
|
uint id;
|
|
|
|
|
|
static uint idGen;
|
2023-05-04 00:48:29 +02:00
|
|
|
|
static size_t cnt;
|
|
|
|
|
|
static size_t check;
|
2023-05-03 15:27:46 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Storage for static ID-Generator
|
|
|
|
|
|
size_t Seg::check{0};
|
2023-05-04 00:48:29 +02:00
|
|
|
|
size_t Seg::cnt{0};
|
2023-05-03 15:27:46 +02:00
|
|
|
|
uint Seg::idGen{0};
|
|
|
|
|
|
|
|
|
|
|
|
const int SMIN = -100;
|
|
|
|
|
|
const int SMAX = +100;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-05-04 00:48:29 +02:00
|
|
|
|
* Test-Segmentation comprised of a sequence of Seg entries.
|
|
|
|
|
|
* It can be checked for _validity_ according to the following conditions
|
|
|
|
|
|
* - the segmentation spans the complete range -100 ... +100
|
|
|
|
|
|
* - segments follow each other _seamlessly_
|
|
|
|
|
|
* - the bounds within each segment are ordered ascending
|
|
|
|
|
|
* - segments are not empty (i.e. start differs from end)
|
|
|
|
|
|
* The assessment of this validity conditions is appended on the string conversion.
|
2023-05-03 15:27:46 +02:00
|
|
|
|
*/
|
|
|
|
|
|
struct SegL
|
|
|
|
|
|
: std::list<Seg>
|
|
|
|
|
|
{
|
|
|
|
|
|
SegL (std::initializer_list<int> breaks)
|
|
|
|
|
|
{
|
|
|
|
|
|
int p = SMIN;
|
|
|
|
|
|
bool bound = true;
|
|
|
|
|
|
for (int b : breaks)
|
|
|
|
|
|
{
|
|
|
|
|
|
emplace_back (p,b, bound);
|
|
|
|
|
|
bound = false;
|
|
|
|
|
|
p = b;
|
|
|
|
|
|
}
|
|
|
|
|
|
emplace_back(p,SMAX, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SegL()
|
|
|
|
|
|
: SegL({})
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
// using standard copy
|
|
|
|
|
|
|
|
|
|
|
|
operator string() const
|
2023-05-04 00:48:29 +02:00
|
|
|
|
{
|
|
|
|
|
|
return renderContent() + assess();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
isValid() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return isnil (this->assess());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
string
|
|
|
|
|
|
renderContent() const
|
2023-05-03 15:27:46 +02:00
|
|
|
|
{
|
|
|
|
|
|
return "├"+util::join(*this,"")+"┤";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
string
|
|
|
|
|
|
assess() const
|
|
|
|
|
|
{
|
|
|
|
|
|
string diagnosis;
|
|
|
|
|
|
if (empty())
|
|
|
|
|
|
diagnosis = "!empty!";
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (front().start != SMIN)
|
|
|
|
|
|
diagnosis += "missing-lower-bound!";
|
|
|
|
|
|
if (back().after != SMAX)
|
|
|
|
|
|
diagnosis += "missing-upper-bound!";
|
|
|
|
|
|
int p = SMIN;
|
|
|
|
|
|
for (auto const& s : *this)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (s.start != p)
|
|
|
|
|
|
diagnosis += _Fmt{"!gap_%i<>%i_!"} % p % s.start;
|
|
|
|
|
|
if (s.start == s.after)
|
|
|
|
|
|
diagnosis += _Fmt{"!degen_%i_!"} % s.start;
|
|
|
|
|
|
if (s.start > s.after)
|
|
|
|
|
|
diagnosis += _Fmt{"!order_%i>%i_!"} % s.start % s.after;
|
|
|
|
|
|
p = s.after;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return diagnosis;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-05-04 12:01:25 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ======= Split/Splice-Algo Setup ======= */
|
|
|
|
|
|
|
|
|
|
|
|
using OptInt = std::optional<int>;
|
|
|
|
|
|
using Iter = typename SegL::iterator;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Perform the »SplitSplice« Algorithm to splice a new Segment
|
|
|
|
|
|
* into the given [segmentation of the integer-axis](\ref SegL).
|
|
|
|
|
|
* A local λ-binding is setup to define the basic operations
|
2023-05-04 15:59:17 +02:00
|
|
|
|
* required by the algorithm implementation to work with this
|
2023-05-04 12:01:25 +02:00
|
|
|
|
* specific kind of data.
|
|
|
|
|
|
* @return Tuple `(s,n,e)` to indicate where changes happened
|
|
|
|
|
|
* - s the first changed element
|
|
|
|
|
|
* - n the new main segment (may be identical to s)
|
|
|
|
|
|
* - e the first unaltered element after the changed range (may be `end()`)
|
|
|
|
|
|
* @see lib::splitsplice::Algo
|
|
|
|
|
|
* @see [setup for the Fixture-Segmentation](\ref steam::fixture::Segmentation::splitSplice)
|
|
|
|
|
|
*/
|
|
|
|
|
|
auto
|
|
|
|
|
|
invokeSplitSplice (SegL& segs, OptInt startNew, OptInt afterNew)
|
|
|
|
|
|
{
|
|
|
|
|
|
/*---configure-contextually-bound-Functors--------------------*/
|
|
|
|
|
|
auto getStart = [](Iter elm) -> int { return elm->start; };
|
|
|
|
|
|
auto getAfter = [](Iter elm) -> int { return elm->after; };
|
|
|
|
|
|
auto createSeg= [&](Iter pos, int start, int after) -> Iter { return segs.emplace (pos, start, after); };
|
|
|
|
|
|
auto emptySeg = [&](Iter pos, int start, int after) -> Iter { return segs.emplace (pos, start, after, true); };
|
|
|
|
|
|
auto cloneSeg = [&](Iter pos, int start, int after, Iter src) -> Iter { return segs.emplace (pos, *src, start, after); };
|
|
|
|
|
|
auto discard = [&](Iter pos, Iter after) -> Iter { return segs.erase (pos,after); };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lib::splitsplice::Algo splicer{ getStart
|
|
|
|
|
|
, getAfter
|
|
|
|
|
|
, createSeg
|
|
|
|
|
|
, emptySeg
|
|
|
|
|
|
, cloneSeg
|
|
|
|
|
|
, discard
|
|
|
|
|
|
, SMAX
|
|
|
|
|
|
, segs.begin(),segs.end()
|
|
|
|
|
|
, startNew, afterNew
|
|
|
|
|
|
};
|
|
|
|
|
|
splicer.determineRelations();
|
|
|
|
|
|
return splicer.performSplitSplice();
|
|
|
|
|
|
}
|
2023-05-03 15:27:46 +02:00
|
|
|
|
}//(End)Test Fixture
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-05-04 00:48:29 +02:00
|
|
|
|
|
2023-05-03 15:27:46 +02:00
|
|
|
|
/****************************************************************************//**
|
|
|
|
|
|
* @test verify proper working of a generic procedure to splice an interval
|
|
|
|
|
|
* into a complete segmentation of an ordered axis into seamless intervals.
|
|
|
|
|
|
* - demonstrate how to setup the invocation with custom data types
|
|
|
|
|
|
* - systematic coverage of all possible arrangements of intervals
|
|
|
|
|
|
* - handling of irregular cases
|
|
|
|
|
|
* @see split-splice.hpp
|
|
|
|
|
|
* @see steam::fixture::Segmentation::splitSplice
|
|
|
|
|
|
*/
|
|
|
|
|
|
class SplitSplice_test : public Test
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
virtual void
|
|
|
|
|
|
run (Arg)
|
|
|
|
|
|
{
|
|
|
|
|
|
demonstrate_usage();
|
|
|
|
|
|
verify_testFixture();
|
|
|
|
|
|
verify_standardCases();
|
|
|
|
|
|
verify_cornerCases();
|
2023-05-05 01:34:41 +02:00
|
|
|
|
verify_integrity();
|
|
|
|
|
|
|
|
|
|
|
|
// no memory leaked
|
|
|
|
|
|
CHECK (0 == Seg::check);
|
|
|
|
|
|
CHECK (0 == Seg::cnt);
|
2023-05-03 15:27:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @test demonstrate how to use the »Split-Splice« algorithm with custom data
|
|
|
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
|
|
|
demonstrate_usage()
|
|
|
|
|
|
{
|
2023-05-04 12:01:25 +02:00
|
|
|
|
SegL segmentation;
|
|
|
|
|
|
CHECK (segmentation == "├[-100~100[┤"_expect);
|
2023-05-04 02:07:38 +02:00
|
|
|
|
|
2023-05-04 12:01:25 +02:00
|
|
|
|
OptInt startNew{5},
|
|
|
|
|
|
afterNew{23};
|
2023-05-04 02:07:38 +02:00
|
|
|
|
|
2023-05-04 12:01:25 +02:00
|
|
|
|
invokeSplitSplice (segmentation, startNew, afterNew);
|
|
|
|
|
|
|
|
|
|
|
|
// The given segmentation was modified by side-effect
|
|
|
|
|
|
// - a new segment [5...23[ has been inserted in the middle
|
|
|
|
|
|
// - suitably adapted empty predecessor and successor segments
|
|
|
|
|
|
CHECK (segmentation == "├[-100~5[[5_23[[23~100[┤"_expect);
|
2023-05-04 02:07:38 +02:00
|
|
|
|
|
2023-05-04 12:01:25 +02:00
|
|
|
|
// The modified segmentation still seamlessly covers the whole axis
|
|
|
|
|
|
CHECK (segmentation.isValid());
|
2023-05-03 15:27:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-05-04 14:43:40 +02:00
|
|
|
|
|
2023-05-03 15:27:46 +02:00
|
|
|
|
/** @test verify the fixture and self-diagnostics for this test */
|
|
|
|
|
|
void
|
|
|
|
|
|
verify_testFixture()
|
|
|
|
|
|
{
|
2023-05-04 02:07:38 +02:00
|
|
|
|
CHECK (0 == Seg::check);
|
|
|
|
|
|
Seg::idGen = 0;
|
2023-05-03 15:27:46 +02:00
|
|
|
|
{
|
2023-05-04 00:48:29 +02:00
|
|
|
|
Seg x{1,3}; // a segment 1 (inclusive) to 3 (exclusive)
|
|
|
|
|
|
Seg u{2,4,true}; // an "empty" segment 2 (incl) to 4 (excl)
|
|
|
|
|
|
CHECK (x == "[1_3["_expect);
|
|
|
|
|
|
CHECK (u == "[2~4["_expect); // "empty" interval is marked with '~' in string stylisation
|
|
|
|
|
|
CHECK (3 == Seg::check);
|
|
|
|
|
|
CHECK (2 == Seg::cnt);
|
|
|
|
|
|
|
2023-05-03 15:27:46 +02:00
|
|
|
|
Seg z{move(u)};
|
2023-05-04 00:48:29 +02:00
|
|
|
|
CHECK (z == "[2~4["_expect);
|
|
|
|
|
|
CHECK (3 == Seg::check);
|
|
|
|
|
|
CHECK (2 == Seg::cnt); // the "dead" instance u is not counted
|
|
|
|
|
|
CHECK (0 == u.id); // (its ID has been reset to zero in move-ctor)
|
|
|
|
|
|
CHECK (2 == z.id);
|
2023-05-03 15:27:46 +02:00
|
|
|
|
|
2023-05-04 00:48:29 +02:00
|
|
|
|
SegL l1; // default ctor always adds an empty base segment -100 ... +100
|
2023-05-03 15:27:46 +02:00
|
|
|
|
SegL l2{3};
|
|
|
|
|
|
SegL l3{5,-5,10};
|
2023-05-04 00:48:29 +02:00
|
|
|
|
CHECK (l1 == "├[-100~100[┤"_expect);
|
|
|
|
|
|
CHECK (l2 == "├[-100~3[[3~100[┤"_expect);
|
|
|
|
|
|
CHECK (l3 == "├[-100~5[[5_-5[[-5_10[[10~100[┤!order_5>-5_!"_expect);
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (l1.isValid());
|
|
|
|
|
|
CHECK (l2.isValid());
|
|
|
|
|
|
CHECK (not l3.isValid()); // l3 violates validity condition, because segment [5 ... -5[ is reversed
|
|
|
|
|
|
CHECK (l3.assess() == "!order_5>-5_!"_expect);
|
2023-05-03 15:27:46 +02:00
|
|
|
|
|
2023-05-04 00:48:29 +02:00
|
|
|
|
CHECK ( 9 == Seg::cnt ); // 9 objects are alive
|
|
|
|
|
|
CHECK ( 9 == Seg::idGen); // ID generator sticks at 9
|
|
|
|
|
|
CHECK (45 == Seg::check); // checksum 1+..+9
|
2023-05-03 15:27:46 +02:00
|
|
|
|
|
|
|
|
|
|
l3.erase(l3.begin());
|
2023-05-04 00:48:29 +02:00
|
|
|
|
CHECK (l3.assess() == "missing-lower-bound!!gap_-100<>5_!!order_5>-5_!"_expect);
|
|
|
|
|
|
CHECK ( 8 == Seg::cnt ); // also one object less alive
|
|
|
|
|
|
|
|
|
|
|
|
l3.begin()->after = 5; // manipulate first segment to make it degenerate (empty
|
|
|
|
|
|
CHECK (l3.renderContent() == "├[5_5[[-5_10[[10~100[┤"_expect);
|
|
|
|
|
|
CHECK (l3.assess() == "missing-lower-bound!!gap_-100<>5_!!degen_5_!!gap_5<>-5_!"_expect);
|
2023-05-03 15:27:46 +02:00
|
|
|
|
l3.clear();
|
2023-05-04 00:48:29 +02:00
|
|
|
|
CHECK (l3.assess() == "!empty!"_expect);
|
2023-05-03 15:27:46 +02:00
|
|
|
|
|
2023-05-04 00:48:29 +02:00
|
|
|
|
CHECK ( 5 == Seg::cnt );
|
|
|
|
|
|
CHECK ( 9 == Seg::idGen);
|
|
|
|
|
|
CHECK (15 == Seg::check);
|
2023-05-03 15:27:46 +02:00
|
|
|
|
}
|
2023-05-04 00:48:29 +02:00
|
|
|
|
// all objects go out of scope
|
|
|
|
|
|
CHECK (0 == Seg::cnt );
|
|
|
|
|
|
CHECK (0 == Seg::check);
|
|
|
|
|
|
CHECK (9 == Seg::idGen);
|
2023-05-03 15:27:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-05-04 14:43:40 +02:00
|
|
|
|
|
2023-05-03 15:27:46 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* @test cover all possible cases of splicing an interval
|
|
|
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
|
|
|
verify_standardCases()
|
|
|
|
|
|
{
|
2023-05-04 14:43:40 +02:00
|
|
|
|
auto testCase = [](SegL segmentation
|
|
|
|
|
|
,int startNew
|
|
|
|
|
|
,int afterNew
|
|
|
|
|
|
,ExpectString expectedResult)
|
|
|
|
|
|
{
|
|
|
|
|
|
OptInt startSpec{startNew},
|
|
|
|
|
|
afterSpec{afterNew};
|
|
|
|
|
|
|
|
|
|
|
|
invokeSplitSplice (segmentation, startSpec, afterSpec);
|
|
|
|
|
|
CHECK (segmentation == expectedResult);
|
|
|
|
|
|
CHECK (segmentation.isValid());
|
|
|
|
|
|
};
|
2023-05-04 15:59:17 +02:00
|
|
|
|
////////
|
2023-05-04 14:43:40 +02:00
|
|
|
|
testCase (SegL{}, -23,24, "├[-100~-23[[-23_24[[24~100[┤"_expect); // simple segment into empty axis
|
|
|
|
|
|
|
2023-05-04 15:59:17 +02:00
|
|
|
|
// insert smaller segment
|
2023-05-04 14:43:40 +02:00
|
|
|
|
testCase (SegL{5,10}, 2,3, "├[-100~2[[2_3[[3~5[[5_10[[10~100[┤"_expect); // smaller segment left spaced off
|
|
|
|
|
|
testCase (SegL{5,10}, 4,5, "├[-100~4[[4_5[[5_10[[10~100[┤"_expect); // left adjacent
|
|
|
|
|
|
testCase (SegL{5,10}, 4,8, "├[-100~4[[4_8[[8_10[[10~100[┤"_expect); // left overlapping
|
|
|
|
|
|
testCase (SegL{5,10}, 5,8, "├[-100~5[[5_8[[8_10[[10~100[┤"_expect); // left inside justified
|
|
|
|
|
|
testCase (SegL{5,10}, 6,8, "├[-100~5[[5_6[[6_8[[8_10[[10~100[┤"_expect); // smaller segment complete inside
|
|
|
|
|
|
testCase (SegL{5,10}, 7,10, "├[-100~5[[5_7[[7_10[[10~100[┤"_expect); // right inside justified
|
|
|
|
|
|
testCase (SegL{5,10}, 9,13, "├[-100~5[[5_9[[9_13[[13~100[┤"_expect); // right overlapping
|
|
|
|
|
|
testCase (SegL{5,10}, 10,13, "├[-100~5[[5_10[[10_13[[13~100[┤"_expect); // right adjacent
|
|
|
|
|
|
testCase (SegL{5,10}, 13,23, "├[-100~5[[5_10[[10~13[[13_23[[23~100[┤"_expect); // right spaced off
|
|
|
|
|
|
|
2023-05-04 15:59:17 +02:00
|
|
|
|
// insert identical segment
|
2023-05-04 14:43:40 +02:00
|
|
|
|
testCase (SegL{5,10}, 5,10, "├[-100~5[[5_10[[10~100[┤"_expect); // identical size replacement
|
|
|
|
|
|
|
2023-05-04 15:59:17 +02:00
|
|
|
|
// insert larger segment
|
2023-05-04 14:43:40 +02:00
|
|
|
|
testCase (SegL{5,10}, 3,10, "├[-100~3[[3_10[[10~100[┤"_expect); // larger segment right aligned
|
|
|
|
|
|
testCase (SegL{5,10}, 3,23, "├[-100~3[[3_23[[23~100[┤"_expect); // larger segment overarching
|
|
|
|
|
|
testCase (SegL{5,10}, 5,23, "├[-100~5[[5_23[[23~100[┤"_expect); // larger segment left aligned
|
2023-05-04 15:59:17 +02:00
|
|
|
|
} //////
|
|
|
|
|
|
|
2023-05-03 15:27:46 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @test cover special and boundary cases
|
|
|
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
|
|
|
verify_cornerCases()
|
|
|
|
|
|
{
|
2023-05-04 15:59:17 +02:00
|
|
|
|
auto testCase = [](SegL segmentation
|
|
|
|
|
|
,OptInt startNew // Note: these are optional...
|
|
|
|
|
|
,OptInt afterNew
|
|
|
|
|
|
,ExpectString expectedResult)
|
|
|
|
|
|
{
|
|
|
|
|
|
invokeSplitSplice (segmentation, startNew, afterNew);
|
|
|
|
|
|
CHECK (segmentation == expectedResult);
|
|
|
|
|
|
CHECK (segmentation.isValid());
|
|
|
|
|
|
};
|
|
|
|
|
|
auto x = std::nullopt;
|
|
|
|
|
|
//////
|
|
|
|
|
|
testCase (SegL{}, 3,2, "├[-100~2[[2_3[[3~100[┤"_expect); // flipped interval spec is reoriented
|
|
|
|
|
|
//////
|
|
|
|
|
|
testCase (SegL{}, 3,x, "├[-100~3[[3_100[┤"_expect); // expanded until domain end
|
|
|
|
|
|
testCase (SegL{}, x,5, "├[-100_5[[5~100[┤"_expect); // expanded to start of domain
|
|
|
|
|
|
/////
|
|
|
|
|
|
testCase (SegL{4,6}, 5,x, "├[-100~4[[4_5[[5_6[[6~100[┤"_expect); // expanded until end of enclosing segment
|
|
|
|
|
|
testCase (SegL{4,6}, x,5, "├[-100~4[[4_5[[5_6[[6~100[┤"_expect); // expanded to start of enclosing segment
|
|
|
|
|
|
/////
|
|
|
|
|
|
testCase (SegL{4,6}, 3,x, "├[-100~3[[3_4[[4_6[[6~100[┤"_expect); // expanded to fill gap to next segment
|
|
|
|
|
|
testCase (SegL{4,6}, x,3, "├[-100_3[[3~4[[4_6[[6~100[┤"_expect); // expanded to cover predecessor completely
|
|
|
|
|
|
testCase (SegL{4,6}, 4,x, "├[-100~4[[4_6[[6~100[┤"_expect); // expanded to cover (replace) successor
|
|
|
|
|
|
testCase (SegL{4,6}, x,4, "├[-100_4[[4_6[[6~100[┤"_expect); // expanded to cover (replace) predecessor
|
|
|
|
|
|
/////
|
|
|
|
|
|
testCase (SegL{4,6}, 7,x, "├[-100~4[[4_6[[6~7[[7_100[┤"_expect); // shorten successor and expand new segment to end of successor (=domain end)
|
|
|
|
|
|
testCase (SegL{4,6}, x,7, "├[-100~4[[4_6[[6_7[[7~100[┤"_expect); // fill gap between predecessor and given new segment end
|
|
|
|
|
|
testCase (SegL{4,6}, 6,x, "├[-100~4[[4_6[[6_100[┤"_expect); // expand to cover (replace) the following segment until domain end
|
|
|
|
|
|
testCase (SegL{4,6}, x,6, "├[-100~4[[4_6[[6~100[┤"_expect); // expanded to cover (replace) the preceding segment
|
|
|
|
|
|
/////
|
|
|
|
|
|
testCase (SegL{}, x,x, "├[-100_100[┤"_expect); // without any specification, the whole domain is covered
|
|
|
|
|
|
testCase (SegL{4}, x,x, "├[-100~4[[4_100[┤"_expect); // otherwise, without any spec the last segment is replaced
|
|
|
|
|
|
testCase (SegL{4,6}, x,x, "├[-100~4[[4_6[[6_100[┤"_expect);
|
|
|
|
|
|
/////
|
|
|
|
|
|
testCase (SegL{4,5,6,8}, 3,6, "├[-100~3[[3_6[[6_8[[8~100[┤"_expect); // spanning and thus replacing multiple segments
|
|
|
|
|
|
testCase (SegL{4,5,6,8}, 4,6, "├[-100~4[[4_6[[6_8[[8~100[┤"_expect);
|
|
|
|
|
|
testCase (SegL{4,5,6,8}, 4,7, "├[-100~4[[4_7[[7_8[[8~100[┤"_expect);
|
|
|
|
|
|
testCase (SegL{4,5,6,8}, 3,7, "├[-100~3[[3_7[[7_8[[8~100[┤"_expect);
|
|
|
|
|
|
testCase (SegL{4,5,6,8}, 3,8, "├[-100~3[[3_8[[8~100[┤"_expect);
|
|
|
|
|
|
testCase (SegL{4,5,6,8}, 4,8, "├[-100~4[[4_8[[8~100[┤"_expect);
|
|
|
|
|
|
testCase (SegL{4,5,6,8}, 4,9, "├[-100~4[[4_9[[9~100[┤"_expect);
|
|
|
|
|
|
testCase (SegL{4,5,6,8}, 5,9, "├[-100~4[[4_5[[5_9[[9~100[┤"_expect);
|
|
|
|
|
|
testCase (SegL{4,5,6,8}, 5,x, "├[-100~4[[4_5[[5_6[[6_8[[8~100[┤"_expect);
|
|
|
|
|
|
testCase (SegL{4,5,7,8}, x,6, "├[-100~4[[4_5[[5_6[[6_7[[7_8[[8~100[┤"_expect);
|
|
|
|
|
|
} /////
|
2023-05-05 01:34:41 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @test verify instance data is properly handled on _split operation_
|
|
|
|
|
|
* - segments not touched will stay at same memory location
|
|
|
|
|
|
* - any adapted segments will be new allocated objects
|
|
|
|
|
|
* - yet segments cloned / split will carry on all data
|
|
|
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
|
|
|
verify_integrity()
|
|
|
|
|
|
{
|
|
|
|
|
|
SegL segs{2,6};
|
|
|
|
|
|
CHECK (segs == "├[-100~2[[2_6[[6~100[┤"_expect);
|
|
|
|
|
|
|
|
|
|
|
|
Iter s = segs.begin();
|
|
|
|
|
|
CHECK (s->start == -100);
|
|
|
|
|
|
CHECK (s->after == 2);
|
|
|
|
|
|
uint id1 = s->id;
|
|
|
|
|
|
void* adr1 = &(*s);
|
|
|
|
|
|
++s;
|
|
|
|
|
|
CHECK (s->start == 2);
|
|
|
|
|
|
CHECK (s->after == 6);
|
|
|
|
|
|
uint id2 = s->id;
|
|
|
|
|
|
void* adr2 = &(*s);
|
|
|
|
|
|
++s;
|
|
|
|
|
|
CHECK (s->start == 6);
|
|
|
|
|
|
CHECK (s->after == 100);
|
|
|
|
|
|
uint id3 = s->id;
|
|
|
|
|
|
void* adr3 = &(*s);
|
|
|
|
|
|
|
|
|
|
|
|
auto [p,n,a] = invokeSplitSplice (segs, 3,4);
|
|
|
|
|
|
CHECK (5 == segs.size());
|
|
|
|
|
|
CHECK (segs == "├[-100~2[[2_3[[3_4[[4_6[[6~100[┤"_expect);
|
|
|
|
|
|
|
|
|
|
|
|
s = segs.begin();
|
|
|
|
|
|
CHECK (s->start == -100);
|
|
|
|
|
|
CHECK (s->after == 2);
|
|
|
|
|
|
CHECK (s->id == id1);
|
2024-11-14 22:10:43 +01:00
|
|
|
|
CHECK (adr1 == getAdr(*s));
|
2023-05-05 01:34:41 +02:00
|
|
|
|
CHECK (s != p);
|
|
|
|
|
|
++s;
|
|
|
|
|
|
CHECK (s == p);
|
|
|
|
|
|
CHECK (s->start == 2);
|
|
|
|
|
|
CHECK (s->after == 3);
|
|
|
|
|
|
CHECK (s->id == id2);
|
2024-11-14 22:10:43 +01:00
|
|
|
|
CHECK (adr2 != getAdr(*s)); // this is the first part of the split segment (new allocation)
|
2023-05-05 01:34:41 +02:00
|
|
|
|
++s;
|
|
|
|
|
|
CHECK (s != p);
|
|
|
|
|
|
CHECK (s == n);
|
|
|
|
|
|
CHECK (s->start == 3);
|
|
|
|
|
|
CHECK (s->after == 4);
|
|
|
|
|
|
CHECK (s->id != id1);
|
|
|
|
|
|
CHECK (s->id != id2);
|
|
|
|
|
|
CHECK (s->id != id3);
|
2024-11-14 22:10:43 +01:00
|
|
|
|
CHECK (adr2 != getAdr(*s));
|
2023-05-05 01:34:41 +02:00
|
|
|
|
++s;
|
|
|
|
|
|
CHECK (s != n);
|
|
|
|
|
|
CHECK (s != a);
|
|
|
|
|
|
CHECK (s->start == 4);
|
|
|
|
|
|
CHECK (s->after == 6);
|
|
|
|
|
|
CHECK (s->id != id1);
|
|
|
|
|
|
CHECK (s->id == id2);
|
|
|
|
|
|
CHECK (s->id != id3);
|
2024-11-14 22:10:43 +01:00
|
|
|
|
CHECK (adr2 != getAdr(*s)); // this is the second part of the split segment (new allocation)
|
2023-05-05 01:34:41 +02:00
|
|
|
|
++s;
|
|
|
|
|
|
CHECK (s == a);
|
|
|
|
|
|
CHECK (s->start == 6);
|
|
|
|
|
|
CHECK (s->after == 100);
|
|
|
|
|
|
CHECK (s->id != id1);
|
|
|
|
|
|
CHECK (s->id != id2);
|
|
|
|
|
|
CHECK (s->id == id3);
|
2024-11-14 22:10:43 +01:00
|
|
|
|
CHECK (adr3 == getAdr(*s));
|
2023-05-05 01:34:41 +02:00
|
|
|
|
++s;
|
|
|
|
|
|
CHECK (s == segs.end());
|
|
|
|
|
|
}
|
2023-05-03 15:27:46 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
LAUNCHER (SplitSplice_test, "unit common");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}} // namespace lib::test
|