swift
testcontainers.cpp
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
11 #include <algorithm>
12 #include <iterator>
13 #include <set>
14 #include <vector>
15 
16 #include <QDateTime>
17 #include <QJsonObject>
18 #include <QList>
19 #include <QSet>
20 #include <QString>
21 #include <QTest>
22 #include <QVector>
23 #include <QtGlobal>
24 
25 #include "../testvalueobject.h"
26 #include "test.h"
27 
30 #include "misc/aviation/callsign.h"
32 #include "misc/collection.h"
33 #include "misc/dictionary.h"
34 #include "misc/iterator.h"
35 #include "misc/math/mathutils.h"
36 #include "misc/range.h"
37 #include "misc/registermetadata.h"
38 #include "misc/sequence.h"
39 
40 using namespace swift::misc;
41 using namespace swift::misc::aviation;
42 using namespace swift::misc::geo;
43 using namespace swift::misc::math;
44 using namespace swift::misc::physical_quantities;
45 
46 namespace MiscTest
47 {
49  class CTestContainers : public QObject
50  {
51  Q_OBJECT
52 
53  public:
55  explicit CTestContainers(QObject *parent = nullptr) : QObject(parent) {}
56 
57  private slots:
58  void initTestCase();
59 
60  void collectionBasics();
61  void sequenceBasics();
62  void joinAndSplit();
63  void findTests();
64  void sortTests();
65  void removeTests();
66  void dictionaryBasics();
67  void timestampList();
68  void offsetTimestampList();
69  };
70 
71  void CTestContainers::initTestCase() { swift::misc::registerMetadata(); }
72 
73  void CTestContainers::collectionBasics()
74  {
76  QVERIFY2(c1.isEmpty(), "Uninitialized collection is empty");
77  auto c2 = c1;
78  QVERIFY2(c1 == c2, "Copy of collection is equal");
79  c1.insert(1);
80  QVERIFY2(c1 != c2, "Different collections are not equal");
81  QVERIFY2(c1.size() == 1, "Collection has expected size");
82  c2.insert(1);
83  QVERIFY2(c1 == c2, "Collections with equal elements are equal");
84  c1.clear();
85  QVERIFY2(c1.isEmpty(), "Cleared collection is empty");
86  c1.insert(2);
87  QVERIFY2(c1 != c2, "Collections with different elements are not equal");
88  c1 = c2;
89  QVERIFY2(c1 == c2, "Copied collection is equal");
90  }
91 
92  void CTestContainers::sequenceBasics()
93  {
94  CSequence<int> s1;
95  QVERIFY2(s1.isEmpty(), "Uninitialized sequence is empty");
96  auto s2 = s1;
97  QVERIFY2(s1 == s2, "Copy of sequence is equal");
98  s1.push_back(1);
99  QVERIFY2(s1 != s2, "Different sequences are not equal");
100  QVERIFY2(s1.size() == 1, "Sequence has expected size");
101  s2.push_back(1);
102  QVERIFY2(s1 == s2, "Sequences with equal elements are equal");
103  s1.clear();
104  QVERIFY2(s1.isEmpty(), "Cleared sequence is empty");
105  s1.push_back(2);
106  QVERIFY2(s1 != s2, "Sequences with different elements are not equal");
107  s1 = s2;
108  QVERIFY2(s1 == s2, "Copied sequence is equal");
109 
110  QVERIFY2((s1[0] = 1), "Subscripted element mutation");
111  QVERIFY2(s1[0] == 1, "Subscripted element has expected value");
112  QVERIFY2(s1.back() == 1, "Last element has expected value");
113  }
114 
115  void CTestContainers::joinAndSplit()
116  {
117  CSequence<int> s1, s2;
118  s1.push_back(1);
119  s1.push_back(2);
120  s1.push_back(3);
121  s2.push_back(4);
122  s2.push_back(5);
123  s2.push_back(6);
124  auto joined = s1.join(s2);
125  s1.push_back(s2);
126  QVERIFY2(s1.size() == 6, "Combine sequences");
127  QVERIFY2(s1 == joined, "Combine sequences");
128 
129  CCollection<int> c1, c2, c3, c4;
130  c1.push_back(1);
131  c1.push_back(2);
132  c1.push_back(3);
133  c1.push_back(4);
134  c1.push_back(5);
135  c1.push_back(6);
136  c2.push_back(1);
137  c2.push_back(2);
138  c2.push_back(3);
139  c3.push_back(4);
140  c3.push_back(5);
141  c3.push_back(6);
142  c4.push_back(10);
143  c4.push_back(20);
144  c4.push_back(30);
145  QVERIFY2(c1.makeUnion(c2) == c1, "Combine collections");
146  QVERIFY2(c2.makeUnion(c3) == c1, "Combine collections");
147  QVERIFY2(c1.intersection(c2) == c2, "Combine collections");
148  QVERIFY2(c1.difference(c2) == c3, "Split collections");
149  c1.insert(c4);
150  QVERIFY2(c1.size() == 9, "Combine collections");
151  c1.remove(c4);
152  QVERIFY2(c1.size() == 6, "Split collections");
153  c1.remove(c2);
154  QVERIFY2(c1 == c3, "Split collections");
155  }
156 
157  void CTestContainers::findTests()
158  {
159  CCallsignSet callsigns;
160  CSequence<CCallsign> found = callsigns.findBy(&CCallsign::asString, "Foo");
161  QVERIFY2(found.isEmpty(), "Empty found");
162  callsigns.push_back(CCallsign("EDDM_TWR"));
163  callsigns.push_back(CCallsign("KLAX_TWR"));
164  found = callsigns.findBy(&CCallsign::asString, "KLAX_TWR");
165  QVERIFY2(found.size() == 1, "found");
166  }
167 
168  void CTestContainers::sortTests()
169  {
170  struct Person
171  {
172  const QString &getName() const { return name; }
173  int getAge() const { return age; }
174  bool operator==(const Person &other) const { return name == other.name && age == other.age; }
175  QString name;
176  int age;
177  };
178  CSequence<Person> list { { "Alice", 33 }, { "Bob", 32 }, { "Cathy", 32 }, { "Dave", 31 }, { "Emily", 31 } };
179  CSequence<Person> sorted { { "Dave", 31 }, { "Emily", 31 }, { "Bob", 32 }, { "Cathy", 32 }, { "Alice", 33 } };
180  QVERIFY2(list.sortedBy(&Person::getAge, &Person::getName) == sorted, "sort by multiple members");
181  }
182 
183  void CTestContainers::removeTests()
184  {
185  const CSequence<int> base { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
186  const CSequence<CSequence<int>> subsets { {}, { 1 }, { 9 }, { 5 }, { 1, 9 },
187  { 1, 5 }, { 5, 9 }, { 1, 2 }, { 8, 9 }, { 4, 5, 6 },
188  { 1, 5, 9 }, { 3, 7 }, { 3, 5, 7 }, base };
189  for (const auto &subset : subsets)
190  {
191  auto copy1 = base, copy2 = base;
192  copy1.removeIfIn(subset);
193  copy2.removeIfInSubset(subset);
194  QVERIFY2(copy1 == copy2, "removeIfInSubset");
195  }
196  }
197 
198  void CTestContainers::dictionaryBasics()
199  {
200  CTestValueObject key1("Key1", "This is key object 1");
201  CTestValueObject key2("Key2", "This is key object 2");
202 
203  CTestValueObject value1("Value1", "This is value object 1");
204  CTestValueObject value2("Value2", "This is value object 2");
205 
207  d1.insert(key1, value1);
208  d1.insert(key2, value2);
209 
211  d3.insert(key1, value1);
212  d3.insert(key2, value2);
213 
215 
216  // Operators
217  QVERIFY2(d1 != d2, "Inequality operator failed");
218  QVERIFY2(d1 == d3, "Equality operator failed");
219 
220  // size
221  QVERIFY2(d1.size() == 2, "size() wrong");
222  QVERIFY2(d1.size() == d1.count(), "size() is different to count()");
223 
224  // clear/empty
225  d1.clear();
226  QVERIFY2(d1.isEmpty(), "clear failed");
227  d1.insert(key1, value1);
228  d1.insert(key2, value2);
229 
230  // keys range
231  auto keys = d1.keys();
232  QVERIFY2(std::distance(keys.begin(), keys.end()) == 2, "keys range size wrong");
233 
234  // keys collection
235  CCollection<CTestValueObject> keyCollection = d1.keys();
236  QVERIFY2(keyCollection.size() == 2, "keys collection size wrong");
237 
238  // keys sequence
239  CSequence<CTestValueObject> keySequence = d1.keys();
240  QVERIFY2(keySequence.size() == 2, "keys sequence size wrong");
241 
242  // findKeyBy
243  d2 = d1.findKeyBy(&CTestValueObject::getName, QString("Key1"));
244  QVERIFY2(d2.size() == 1, "findKeyBy returned wrong container");
245  CTestValueObject o1 = d2.value(key1);
246  QVERIFY2(o1.getName() == "Value1", "findKeyBy returned wrong container");
247 
248  // findValueBy
249  d2 = d1.findValueBy(&CTestValueObject::getName, QString("Value1"));
250  QVERIFY2(d2.size() == 1, "findValueBy returned wrong container");
251  o1 = d2.value(key1);
252  QVERIFY2(o1.getName() == "Value1", "findKeyBy returned wrong container");
253 
254  // containsByKey
255  QVERIFY2(d1.containsByKey(&CTestValueObject::getName, QString("Key1")), "containsByKey failed");
256  QVERIFY2(!d1.containsByKey(&CTestValueObject::getName, QString("Key5")), "containsByKey failed");
257 
258  // containsByValue
259  QVERIFY2(d1.containsByValue(&CTestValueObject::getName, QString("Value1")), "containsByValue failed");
260  QVERIFY2(!d1.containsByValue(&CTestValueObject::getName, QString("Value5")), "containsByValue failed");
261 
262  // removeByKeyIf
263  d2 = d1;
265  QVERIFY2(d2.size() == 1, "size() wrong");
266 
267  // removeByValueIf
268  d2 = d1;
270  QVERIFY2(d2.size() == 1, "size() wrong");
271 
272  // JSON
273  QJsonObject jsonObject = d1.toJson();
275  d4.convertFromJson(jsonObject);
276  QVERIFY2(d1 == d4, "JSON serialization/deserialization failed");
277  }
278 
279  void CTestContainers::timestampList()
280  {
281  CAircraftSituationList situations;
282  const qint64 ts = QDateTime::currentMSecsSinceEpoch();
283  int no = 10;
284  for (int i = 0; i < no; ++i)
285  {
287  s.setCallsign("CS" + QString::number(i));
288  s.setMSecsSinceEpoch(ts - 10 * i);
289  situations.push_back(s);
290  }
291 
292  // test sorting
293  situations.sortOldestFirst();
294  qint64 ms = situations.front().getMSecsSinceEpoch();
295  QVERIFY2(ms == ts - 10 * (no - 1), "Oldest value not first");
296 
297  situations.sortLatestFirst();
298  ms = situations.front().getMSecsSinceEpoch();
299  QVERIFY2(ms == ts, "Latest value not first");
300 
301  // test shifting
302  situations.clear();
303  const int maxElements = 8;
304  QVERIFY(situations.isEmpty());
305  for (int i = 0; i < no; ++i)
306  {
307  qint64 cTs = ts - 10 * i;
309  s.setCallsign("CS" + QString::number(i));
310  s.setMSecsSinceEpoch(cTs);
311  situations.push_frontMaxElements(s, maxElements);
312  if (i > maxElements - 1)
313  {
314  QVERIFY2(situations.size() == maxElements, "Situations must only contain max.elements");
315  }
316  else
317  {
318  QVERIFY2(situations.size() == i + 1, "Element size does not match");
319  QVERIFY2(situations.front().getMSecsSinceEpoch() == cTs, "Wrong front element");
320  }
321  }
322 
323  situations.sortLatestFirst();
324  QVERIFY2(situations.isSortedLatestFirst(), "Espect sorted latest first");
325  no = situations.size();
326  for (int i = 0; i < no; ++i)
327  {
328  const CAircraftSituation current = situations[i];
329  const qint64 cTs = current.getMSecsSinceEpoch();
330  const CAircraftSituation expectedBefore = situations.findObjectBeforeOrDefault(cTs);
331  const CAircraftSituation expectedAfter = situations.findObjectAfterOrDefault(cTs);
332  const qint64 beforeTs = expectedBefore.getMSecsSinceEpoch();
333  const qint64 afterTs = expectedAfter.getMSecsSinceEpoch();
334 
335  if (i > 0)
336  {
337  const qint64 t1 = situations[i - 1].getMSecsSinceEpoch();
338  QVERIFY2(t1 == afterTs, "Wrong expected after");
339  }
340  if (i < (no - 1))
341  {
342  const qint64 t1 = situations[i + 1].getMSecsSinceEpoch();
343  QVERIFY2(t1 == beforeTs, "Wrong expected before");
344  }
345  }
346  }
347 
348  void CTestContainers::offsetTimestampList()
349  {
350  CAircraftSituationList situations;
351  static const CCoordinateGeodetic geoPos =
352  CCoordinateGeodetic::fromWgs84("48° 21′ 13″ N", "11° 47′ 09″ E", { 1487, CLengthUnit::ft() });
353  qint64 ts = 1000000;
354  int no = 10;
355  const int max = 6;
356 
357  for (int i = 0; i < no; ++i)
358  {
360  s.setPosition(geoPos);
361  s.setMSecsSinceEpoch(ts);
362  s.setCallsign("CS" + QString::number(i));
363 
364  if (CMathUtils::randomBool())
365  {
366  ts += CMathUtils::randomInteger(4500, 5500);
367  s.setTimeOffsetMs(6000);
368  }
369  else
370  {
371  ts += CMathUtils::randomInteger(900, 1100);
372  s.setTimeOffsetMs(2000);
373  }
374 
375  situations.push_frontKeepLatestFirstAdjustOffset(s, true, max);
376 
377  QVERIFY2(situations.size() <= max, "Wrong size");
378  QVERIFY2(situations.isSortedAdjustedLatestFirstWithoutNullPositions(), "Wrong sort order");
379  QVERIFY2(!situations.hasInvalidTimestamps(), "Missing timestamps");
380  QVERIFY2(!situations.containsZeroOrNegativeOffsetTime(), "Missing offset time");
381  }
382 
383  no = situations.size();
384  for (int i = 0; i < no; ++i)
385  {
386  const CAircraftSituation current = situations[i];
387  const qint64 cTs = current.getAdjustedMSecsSinceEpoch();
388  const CAircraftSituation expectedBefore = situations.findObjectBeforeAdjustedOrDefault(cTs);
389  const CAircraftSituation expectedAfter = situations.findObjectAfterAdjustedOrDefault(cTs);
390  const qint64 beforeTs = expectedBefore.getAdjustedMSecsSinceEpoch();
391  const qint64 afterTs = expectedAfter.getAdjustedMSecsSinceEpoch();
392 
393  if (i > 0)
394  {
395  const qint64 t1 = situations[i - 1].getAdjustedMSecsSinceEpoch();
396  QVERIFY2(t1 == afterTs, "Wrong expected after");
397  }
398  if (i < (no - 1))
399  {
400  const qint64 t1 = situations[i + 1].getAdjustedMSecsSinceEpoch();
401  QVERIFY2(t1 == beforeTs, "Wrong expected before");
402  }
403  }
404  }
405 } // namespace MiscTest
406 
409 
410 #include "testcontainers.moc"
411 
Testing containers.
CTestContainers(QObject *parent=nullptr)
Constructor.
Generic ordered container with value semantics.
Definition: collection.h:107
size_type size() const
Returns number of elements in the collection.
Definition: collection.h:185
CCollection difference(const C &other) const
Returns a collection which contains all the elements from this collection which are not in the other ...
Definition: collection.h:278
void remove(const T &object)
Efficient remove using the find and erase of the implementation container. Typically O(log n).
Definition: collection.h:307
iterator insert(const_iterator hint, const T &value)
For compatibility with std::inserter.
Definition: collection.h:199
CCollection intersection(const C &other) const
Returns a collection which is the intersection of this collection and another.
Definition: collection.h:268
bool isEmpty() const
Synonym for empty.
Definition: collection.h:191
CCollection makeUnion(const C &other) const
Returns a collection which is the union of this collection and another container.
Definition: collection.h:259
iterator push_back(const T &value)
Synonym for insert.
Definition: collection.h:238
void clear()
Removes all elements in the collection.
Definition: collection.h:194
Associative container with value semantics, chooses a sensible default implementation container type.
Definition: dictionary.h:83
int count(const Key &key) const
Returns the number of items with key.
Definition: dictionary.h:362
void removeByKeyIf(Predicate p)
Remove elements for which a given predicate for value returns true.
Definition: dictionary.h:191
CDictionary findValueBy(Predicate p) const
Return a copy containing only those elements for which a given predicate returns true.
Definition: dictionary.h:141
void clear()
Removes all items from the dictionary.
Definition: dictionary.h:338
const Value value(const Key &key) const
Returns the value associated with the key.
Definition: dictionary.h:404
void convertFromJson(const QJsonObject &json)
Assign from JSON object.
Definition: dictionary.h:248
int size() const
Returns the number of items in the hash.
Definition: dictionary.h:398
QJsonObject toJson() const
Cast to JSON object.
Definition: dictionary.h:237
CDictionary findKeyBy(Predicate p) const
Return a copy containing only those elements for which the dictionary keys return true for a given pr...
Definition: dictionary.h:120
auto keys() const
Return a range of all keys (does not allocate a temporary container)
Definition: dictionary.h:392
bool isEmpty() const
Returns true if dictionary is empty.
Definition: dictionary.h:383
iterator insert(const Key &key, const Value &value)
Insert new item with key and value.
Definition: dictionary.h:374
void removeByValueIf(Predicate p)
Remove elements for which a given predicate for key returns true.
Definition: dictionary.h:202
bool containsByValue(Predicate p) const
Return true if there is an element for which a given predicate returns true.
Definition: dictionary.h:177
bool containsByKey(Predicate p) const
Return true if there is an element for which a given predicate returns true.
Definition: dictionary.h:162
auto findBy(Predicate p) const
Return a copy containing only those elements for which a given predicate returns true.
Definition: range.h:410
Generic sequential container with value semantics.
Definition: sequence.h:86
size_type size() const
Returns number of elements in the sequence.
Definition: sequence.h:273
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
void push_frontMaxElements(const T &value, int maxElements)
Insert as first element by keep maxElements.
Definition: sequence.h:314
reference front()
Access the first element.
Definition: sequence.h:225
void clear()
Removes all elements in the sequence.
Definition: sequence.h:288
reference back()
Access the last element.
Definition: sequence.h:249
int removeIfIn(const CSequence &other)
Remove all elements if they are in other.
Definition: sequence.h:464
bool isEmpty() const
Synonym for empty.
Definition: sequence.h:285
CSequence join(const CSequence &other) const
Concatenates two sequences and returns the result.
Definition: sequence.h:350
const QString & getName() const
Get name.
qint64 getMSecsSinceEpoch() const
Timestamp as ms value.
void setMSecsSinceEpoch(qint64 mSecsSinceEpoch)
Timestamp as ms value.
OBJ findObjectBeforeOrDefault(qint64 msSinceEpoch) const
Object before timestamp or default (older)
void sortOldestFirst()
Sort by timestamp.
OBJ findObjectAfterOrDefault(qint64 msSinceEpoch) const
List of objects after msSinceEpoch (newer)
void sortLatestFirst()
Sort by timestamp.
bool hasInvalidTimestamps() const
Has invalid timestamp.
bool isSortedLatestFirst() const
Is completely sorted: latest last.
qint64 getAdjustedMSecsSinceEpoch() const
Timestamp with offset added for interpolation.
void setTimeOffsetMs(qint64 offset)
Milliseconds to add to timestamp for interpolation.
bool containsZeroOrNegativeOffsetTime() const
Any negative or zero offset time?
OBJ findObjectBeforeAdjustedOrDefault(qint64 msSinceEpoch) const
Object before timestamp (older)
void push_frontKeepLatestFirstAdjustOffset(const OBJ &value, bool replaceSameTimestamp=true, int maxElements=-1)
Insert as first element by keeping maxElements and the latest first.
OBJ findObjectAfterAdjustedOrDefault(qint64 msSinceEpoch) const
List of objects after msSinceEpoch (newer)
Value object encapsulating information of an aircraft's situation.
void setCallsign(const CCallsign &callsign)
Corresponding callsign.
void setPosition(const geo::CCoordinateGeodetic &position)
Set position.
bool isSortedAdjustedLatestFirstWithoutNullPositions() const
Latest first and no null positions?
Value object encapsulating information of a callsign.
Definition: callsign.h:30
const QString & asString() const
Get callsign (normalized)
Definition: callsign.h:96
Value object for a set of callsigns.
Definition: callsignset.h:26
Free functions in swift::misc.
void registerMetadata()
Register all relevant metadata in Misc.
SWIFTTEST_APPLESS_MAIN(MiscTest::CTestContainers)
main