timewarrior/test/data.t.cpp
Thomas Lauf 6d79ea4a6d Update copyright statements
Signed-off-by: Thomas Lauf <thomas.lauf@tngtech.com>
2023-09-18 12:33:17 +02:00

306 lines
16 KiB
C++

////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 - 2019, 2022, Thomas Lauf, Paul Beckingham, Federico Hernandez.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#include <IntervalFactory.h>
#include <test.h>
#include <timew.h>
////////////////////////////////////////////////////////////////////////////////
void test_flatten (
UnitTest& t,
const std::string& label,
const std::string& input,
const std::vector <Range>& exclusions,
const std::vector <std::string>& output)
{
Interval i;
i = IntervalFactory::fromSerialization (input);
auto results = flatten (i, exclusions);
t.is (results.size (), output.size (), "flatten: " + label + " expected number of results");
for (unsigned int i = 0; i < std::min (output.size (), results.size ()); ++i)
{
Interval tmp;
tmp = IntervalFactory::fromSerialization (output[i]);
t.is (tmp.start.toISO (), results[i].start.toISO (), "flatten: " + label + " start matches");
t.is (tmp.end.toISO (), results[i].end.toISO (), "flatten: " + label + " end matches");
t.ok (tmp.tags () == results[i].tags (), "flatten: " + label + " tags match");
}
}
////////////////////////////////////////////////////////////////////////////////
void test_merge (
UnitTest& t,
const std::string& label,
const std::vector <Range>& input,
const std::vector <Range>& output)
{
auto results = merge (input);
t.is (results.size (), output.size (), "merge: " + label + " expected number of results");
for (unsigned int i = 0; i < std::min (output.size (), results.size ()); ++i)
{
t.is (output[i].start.toISO (), results[i].start.toISO (), "merge: " + label + " start matches");
t.is (output[i].end.toISO (), results[i].end.toISO (), "merge: " + label + " end matches");
}
}
////////////////////////////////////////////////////////////////////////////////
int main (int, char**)
{
UnitTest t ((7 + 7 + 7 + 4 + 4 + 10) +
(1 + 3 + 5 + 5 + 3 + 3 + 7) +
30);
// std::vector <Interval> flatten (const Interval&, std::vector <Range>&);
// input [---------------------------------------------------)
// exc [----------) [---) [---------------)
// output [----------) [---------)
test_flatten (t,
"[1] (full day) - (3 non-overlapping exc) = (2 inc)",
"inc 20160427T000000Z - 20160428T000000Z # foo",
{{Datetime ("20160427T000000Z"), Datetime ("20160427T080000Z")},
{Datetime ("20160427T120000Z"), Datetime ("20160427T130000Z")},
{Datetime ("20160427T173000Z"), Datetime ("20160428T000000Z")}},
{"inc 20160427T080000Z - 20160427T120000Z # foo",
"inc 20160427T130000Z - 20160427T173000Z # foo"});
// input [-------)
// exc [----------) [---) [---------------)
// output [-) [-)
test_flatten (t,
"[2] (inc) - (1 enclosed exc) = (2 inc)",
"inc 20160427T115500Z - 20160427T130500Z # foo",
{{Datetime ("20160427T000000Z"), Datetime ("20160427T080000Z")},
{Datetime ("20160427T120000Z"), Datetime ("20160427T130000Z")},
{Datetime ("20160427T173000Z"), Datetime ("20160428T000000Z")}},
{"inc 20160427T115500Z - 20160427T120000Z # foo",
"inc 20160427T130000Z - 20160427T130500Z # foo"});
// input [...
// exc [----------) [---) [---------------)
// output [-) [...
test_flatten (t,
"[3] (open inc) - (1 overlapping exc) = (2 inc)",
"inc 20160427T160000Z # foo",
{{Datetime ("20160427T000000Z"), Datetime ("20160427T080000Z")},
{Datetime ("20160427T120000Z"), Datetime ("20160427T130000Z")},
{Datetime ("20160427T173000Z"), Datetime ("20160428T000000Z")}},
{"inc 20160427T160000Z - 20160427T173000Z # foo",
"inc 20160428T000000Z # foo"});
// Exclusion encloses interval. Should have no effect.
// input [--)
// exc [----------) [---) [---------------)
// output [--)
test_flatten (t,
"[4] (inc) - (1 enclosing exc) = (unmodified inc)",
"inc 20160427T031500Z - 20160427T041500Z # foo",
{{Datetime ("20160427T000000Z"), Datetime ("20160427T080000Z")},
{Datetime ("20160427T120000Z"), Datetime ("20160427T130000Z")},
{Datetime ("20160427T173000Z"), Datetime ("20160428T000000Z")}},
{"inc 20160427T031500Z - 20160427T041500Z # foo"});
// Multiple overlapping exclusions, no effect.
// input [----)
// exc [---------------------------------------------------)
// exc [----)
// output [----)
test_flatten (t,
"[5] (inc) - (2 overlapping exc) = (unmodified inc)",
"inc 20160512T083000Z - 20160512T093000Z # foo",
{{Datetime ("20160512T080000Z"), Datetime ("20160512T090000Z")}, // One hour.
{Datetime ("20160512T000000Z"), Datetime ("20160513T000000Z")}}, // All day.
{"inc 20160512T083000Z - 20160512T093000Z # foo"});
// Long-running open inclusion, multiple enclosed exclusions, split.
// input [...
// exc [---) [---) [---)
// output [-----) [---) [...
test_flatten (t,
"[6] (inc) - (1 overlapping exc, 2 enclosed exc) = (3 inc)",
"inc 20160523T100000Z # foo",
{{Datetime ("20160523T080000Z"), Datetime ("20160523T120000Z")},
{Datetime ("20160523T160000Z"), Datetime ("20160523T170000Z")},
{Datetime ("20160523T213000Z"), Datetime ("20160524T040000Z")}},
{"inc 20160523T100000Z - 20160523T160000Z # foo",
"inc 20160523T170000Z - 20160523T213000Z # foo",
"inc 20160524T040000Z # foo"});
// Simple range merging.
test_merge (t,
"Empty range",
{},
{});
test_merge (t,
"Single range",
{{Datetime ("20160704T000000Z"), Datetime ("20160704T010000Z")}},
{{Datetime ("20160704T000000Z"), Datetime ("20160704T010000Z")}});
test_merge (t,
"Non-overlapping ranges",
{{Datetime ("20160704T000000Z"), Datetime ("20160704T010000Z")},
{Datetime ("20160704T020000Z"), Datetime ("20160704T030000Z")}},
{{Datetime ("20160704T000000Z"), Datetime ("20160704T010000Z")},
{Datetime ("20160704T020000Z"), Datetime ("20160704T030000Z")}});
test_merge (t,
"Non-overlapping unsorted ranges",
{{Datetime ("20160704T020000Z"), Datetime ("20160704T030000Z")},
{Datetime ("20160704T000000Z"), Datetime ("20160704T010000Z")}},
{{Datetime ("20160704T000000Z"), Datetime ("20160704T010000Z")},
{Datetime ("20160704T020000Z"), Datetime ("20160704T030000Z")}});
test_merge (t,
"Overlapping unsorted ranges",
{{Datetime ("20160704T010000Z"), Datetime ("20160704T030000Z")},
{Datetime ("20160704T000000Z"), Datetime ("20160704T020000Z")}},
{{Datetime ("20160704T000000Z"), Datetime ("20160704T030000Z")}});
test_merge (t,
"Multiple overlapping ranges",
{{Datetime ("20160704T010000Z"), Datetime ("20160704T040000Z")},
{Datetime ("20160704T020000Z"), Datetime ("20160704T050000Z")},
{Datetime ("20160704T030000Z"), Datetime ("20160704T060000Z")}},
{{Datetime ("20160704T010000Z"), Datetime ("20160704T060000Z")}});
test_merge (t,
"Multiple overlapping, enveloping ranges",
{{Datetime ("20160703T000000Z"), Datetime ("20160703T010000Z")},
{Datetime ("20160704T000000Z"), Datetime ("20160705T000000Z")},
{Datetime ("20160704T010000Z"), Datetime ("20160704T040000Z")},
{Datetime ("20160704T020000Z"), Datetime ("20160704T050000Z")},
{Datetime ("20160706T000000Z"), Datetime ("20160706T010000Z")}},
{{Datetime ("20160703T000000Z"), Datetime ("20160703T010000Z")},
{Datetime ("20160704T000000Z"), Datetime ("20160705T000000Z")},
{Datetime ("20160706T000000Z"), Datetime ("20160706T010000Z")}});
// bool matchesFilter (const Interval& interval, const Interval& filter);
Interval refOpen;
refOpen.setRange (Datetime (2016, 6, 1), Datetime (0));
refOpen.tag ("tag1");
refOpen.tag ("tag2");
Interval refClosed;
refClosed.setRange (Datetime (2016, 6, 1), Datetime (2016, 6, 30));
refClosed.tag ("tag1");
refClosed.tag ("tag2");
t.ok (matchesFilter (refOpen, refClosed), "matchesFilter 2016-06-01- tag1 tag2 <=> 2016-06-01-2016-06-30 tag1 tag2");
// this [--------)
// A [--------)
// B [--------)
// C [----)
// D [--------)
// E [--------)
// F [-------------)
// G [...
// H [...
// I [...
Range testA; testA.start = Datetime (2016, 4, 1); testA.end = Datetime (2016, 4, 30);
Range testB; testB.start = Datetime (2016, 5, 15); testB.end = Datetime (2016, 6, 15);
Range testC; testC.start = Datetime (2016, 6, 10); testC.end = Datetime (2016, 6, 20);
Range testD; testD.start = Datetime (2016, 6, 15); testD.end = Datetime (2016, 7, 15);
Range testE; testE.start = Datetime (2016, 8, 1); testE.end = Datetime (2016, 8, 31);
Range testF; testF.start = Datetime (2016, 5, 15); testF.end = Datetime (2016, 7, 15);
Range testG; testG.start = Datetime (2016, 5, 15);
Range testH; testH.start = Datetime (2016, 6, 15);
Range testI; testI.start = Datetime (2016, 7, 15);
Interval i;
i.tag ("tag1");
i.tag ("tag2");
i.setRange (testA); t.notok (matchesFilter (i, refClosed), "matchesFilter A <!> refClosed");
i.setRange (testB); t.ok (matchesFilter (i, refClosed), "matchesFilter B <=> refClosed");
i.setRange (testC); t.ok (matchesFilter (i, refClosed), "matchesFilter C <=> refClosed");
i.setRange (testD); t.ok (matchesFilter (i, refClosed), "matchesFilter D <=> refClosed");
i.setRange (testE); t.notok (matchesFilter (i, refClosed), "matchesFilter E <!> refClosed");
i.setRange (testF); t.ok (matchesFilter (i, refClosed), "matchesFilter F <=> refClosed");
i.setRange (testG); t.ok (matchesFilter (i, refClosed), "matchesFilter G <=> refClosed");
i.setRange (testH); t.ok (matchesFilter (i, refClosed), "matchesFilter H <=> refClosed");
i.setRange (testI); t.notok (matchesFilter (i, refClosed), "matchesFilter I <!> refClosed");
// this [...
// A [--------)
// B [--------)
// C [----)
// D [--------)
// E [--------)
// F [-------------)
// G [...
// H [...
// I [...
i.setRange (testA); t.notok (matchesFilter (i, refOpen), "matchesFilter A <!> refOpen");
i.setRange (testB); t.ok (matchesFilter (i, refOpen), "matchesFilter B <=> refOpen");
i.setRange (testC); t.ok (matchesFilter (i, refOpen), "matchesFilter C <=> refOpen");
i.setRange (testD); t.ok (matchesFilter (i, refOpen), "matchesFilter D <=> refOpen");
i.setRange (testE); t.ok (matchesFilter (i, refOpen), "matchesFilter E <=> refOpen");
i.setRange (testF); t.ok (matchesFilter (i, refOpen), "matchesFilter F <=> refOpen");
i.setRange (testG); t.ok (matchesFilter (i, refOpen), "matchesFilter G <=> refOpen");
i.setRange (testH); t.ok (matchesFilter (i, refOpen), "matchesFilter H <=> refOpen");
i.setRange (testI); t.ok (matchesFilter (i, refOpen), "matchesFilter I <=> refOpen");
// Range getFullDay (const Datetime&);
auto r1 = getFullDay (Datetime ("20160501T203112"));
t.ok (r1.start == Datetime ("20160501T000000"), "getFullDay 2016-05-01T20:31:23 -> start 2016-05-01T00:00:00");
t.ok (r1.end == Datetime ("20160502T000000"), "getFullDay 2016-05-01T20:31:23 -> end 2016-05-02T00:00:00");
// std::vector <Range> subtractRanges (const Range&, const std::vector <Range>&, const std::vector <Range>&);
// Subtract three non-overlapping ranges from a full day, yielding two resultant ranges.
Range limit (Datetime ("20160101T000000"), Datetime ("20160101T235959"));
std::vector <Range> exclusions = {{Datetime ("20160101T000000"), Datetime ("20160101T080000")},
{Datetime ("20160101T120000"), Datetime ("20160101T130000")},
{Datetime ("20160101T173000"), Datetime ("20160101T235959")}};
auto subtracted = subtractRanges ({limit}, exclusions);
t.ok (subtracted.size () == 2, "subtractRanges: all_day - 3 non-adjacent ranges = 2 ranges");
t.ok (subtracted[0].start == Datetime ("20160101T080000"), "subtractRanges: results[0].start = 20160101T080000");
t.ok (subtracted[0].end == Datetime ("20160101T120000"), "subtractRanges: results[0].end = 20160101T120000");
t.ok (subtracted[1].start == Datetime ("20160101T130000"), "subtractRanges: results[1].start = 20160101T130000");
t.ok (subtracted[1].end == Datetime ("20160101T173000"), "subtractRanges: results[1].end = 20160101T173000");
// Subtract a set of overlapping ranges.
exclusions = {{Datetime ("20160101T120000"), Datetime ("20160102T120000")},
{Datetime ("20160101T130000"), Datetime ("20160102T120000")},
{Datetime ("20160101T140000"), Datetime ("20160102T120000")}};
subtracted = subtractRanges ({limit}, exclusions);
t.ok (subtracted.size () == 1, "subtractRanges: all_day - 3 overlapping ranges = 1 range");
t.ok (subtracted[0].start == Datetime ("20160101T000000"), "subtractRanges: results[0].start = 20160101T080000");
t.ok (subtracted[0].end == Datetime ("20160101T120000"), "subtractRanges: results[0].end = 20160101T120000");
// Subtract a single range that extends before and after the limit, yielding
// no results.
exclusions = {{Datetime ("20151201T000000"), Datetime ("20160201T000000")}};
subtracted = subtractRanges ({limit}, exclusions);
t.ok (subtracted.empty (), "subtractRanges: all_day - 2 overlapping months = 0 ranges");
return 0;
}
////////////////////////////////////////////////////////////////////////////////