LeechCraft  0.6.70-18450-gabe19ee3b0
Modular cross-platform feature rich live environment.
oraltest.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  * LeechCraft - modular cross-platform feature rich internet client.
3  * Copyright (C) 2006-2014 Georg Rudoy
4  *
5  * Distributed under the Boost Software License, Version 1.0.
6  * (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
7  **********************************************************************/
8 
9 #include "oraltest.h"
10 #include "common.h"
11 
12 QTEST_GUILESS_MAIN (LC::Util::OralTest)
13 
14 using LC::operator""_ct;
15 
17 {
19  QString Value_;
20 
21  constexpr static auto ClassName = "AutogenPKeyRecord"_ct;
22 
23  auto AsTuple () const
24  {
25  return std::tie (ID_, Value_);
26  }
27 };
28 
30  ID_,
31  Value_)
32 
34 
35 struct NoPKeyRecord
36 {
37  int ID_;
38  QString Value_;
39 
40  constexpr static auto ClassName = "NoPKeyRecord"_ct;
41 
42  auto AsTuple () const
43  {
44  return std::tie (ID_, Value_);
45  }
46 };
47 
48 ORAL_ADAPT_STRUCT (NoPKeyRecord,
49  ID_,
50  Value_)
51 
52 TOSTRING (NoPKeyRecord)
53 
54 struct NonInPlaceConstructibleRecord
55 {
56  int ID_;
57  QString Value_;
58 
59  NonInPlaceConstructibleRecord () = default;
60 
61  NonInPlaceConstructibleRecord (int id, const QString& value, double someExtraArgument)
62  : ID_ { id }
63  , Value_ { value }
64  {
65  Q_UNUSED (someExtraArgument)
66  }
67 
68  constexpr static auto ClassName = "NonInPlaceConstructibleRecord"_ct;
69 
70  auto AsTuple () const
71  {
72  return std::tie (ID_, Value_);
73  }
74 };
75 
76 ORAL_ADAPT_STRUCT (NonInPlaceConstructibleRecord,
77  ID_,
78  Value_)
79 
80 TOSTRING (NonInPlaceConstructibleRecord)
81 
82 struct ConstrainedAutogenPKeyRecord
83 {
84  lco::PKey<int> ID_ {};
86  int Population_;
87 
88  constexpr static auto ClassName = "ConstrainedAutogenPKeyRecord"_ct;
89 
90  auto AsTuple () const
91  {
92  return std::tie (ID_, City_, Population_);
93  }
94 };
95 
96 ORAL_ADAPT_STRUCT (ConstrainedAutogenPKeyRecord,
97  ID_,
98  City_,
99  Population_)
100 
101 TOSTRING (ConstrainedAutogenPKeyRecord)
102 
103 struct OptionalFieldRecord
104 {
105  lco::PKey<int> ID_;
106  QString Name_;
107  std::optional<QString> NickName_;
108  std::optional<QByteArray> Extra_;
109 
110  constexpr static auto ClassName = "OptionalFieldRecord"_ct;
111 
112  auto AsTuple () const
113  {
114  return std::tie (ID_, Name_, NickName_, Extra_);
115  }
116 };
117 
118 ORAL_ADAPT_STRUCT (OptionalFieldRecord,
119  ID_,
120  Name_,
121  NickName_,
122  Extra_)
123 
124 TOSTRING (OptionalFieldRecord)
125 
126 struct ComplexConstraintsRecord
127 {
128  int ID_;
129  QString Name_;
130  QString City_;
131  int Age_;
132  int Weight_;
133 
134  constexpr static auto ClassName = "ComplexConstraintsRecord"_ct;
135 
136  auto AsTuple () const
137  {
138  return std::tie (ID_, Name_, City_, Age_, Weight_);
139  }
140 
144  >;
145 };
146 
147 ORAL_ADAPT_STRUCT (ComplexConstraintsRecord,
148  ID_,
149  Name_,
150  City_,
151  Age_,
152  Weight_)
153 
154 TOSTRING (ComplexConstraintsRecord)
155 
156 namespace LC
157 {
158 namespace Util
159 {
160  namespace sph = oral::sph;
161 
162  void OralTest::testAutoPKeyRecordInsertSelect ()
163  {
164  qDebug () << oral::detail::FieldNames<AutogenPKeyRecord>;
165  auto adapted = PrepareRecords<AutogenPKeyRecord> (MakeDatabase ());
166  const auto& list = adapted->Select ();
167  QCOMPARE (list, (QList<AutogenPKeyRecord> { { 1, "0" }, { 2, "1" }, { 3, "2" } }));
168  }
169 
170  void OralTest::testAutoPKeyRecordInsertRvalueReturnsPKey ()
171  {
172  auto adapted = Util::oral::AdaptPtr<AutogenPKeyRecord, OralFactory> (MakeDatabase ());
173 
174  QList<int> ids;
175  for (int i = 0; i < 3; ++i)
176  ids << adapted->Insert ({ 0, QString::number (i) });
177 
178  QCOMPARE (ids, (QList<int> { 1, 2, 3 }));
179  }
180 
181  void OralTest::testAutoPKeyRecordInsertConstLvalueReturnsPKey ()
182  {
183  auto adapted = Util::oral::AdaptPtr<AutogenPKeyRecord, OralFactory> (MakeDatabase ());
184 
185  QList<AutogenPKeyRecord> records;
186  for (int i = 0; i < 3; ++i)
187  records.push_back ({ 0, QString::number (i) });
188 
189  QList<int> ids;
190  for (const auto& record : records)
191  ids << adapted->Insert (record);
192 
193  QCOMPARE (ids, (QList<int> { 1, 2, 3 }));
194  }
195 
196  void OralTest::testAutoPKeyRecordInsertSetsPKey ()
197  {
198  auto adapted = Util::oral::AdaptPtr<AutogenPKeyRecord, OralFactory> (MakeDatabase ());
199 
200  QList<AutogenPKeyRecord> records;
201  for (int i = 0; i < 3; ++i)
202  records.push_back ({ 0, QString::number (i) });
203 
204  for (auto& record : records)
205  adapted->Insert (record);
206 
207  QCOMPARE (records, (QList<AutogenPKeyRecord> { { 1, "0" }, { 2, "1" }, { 3, "2" } }));
208  }
209 
210  void OralTest::testNoPKeyRecordInsertSelect ()
211  {
212  auto adapted = PrepareRecords<NoPKeyRecord> (MakeDatabase ());
213  const auto& list = adapted->Select ();
214  QCOMPARE (list, (QList<NoPKeyRecord> { { 0, "0" }, { 1, "1" }, { 2, "2" } }));
215  }
216 
217  void OralTest::testNonInPlaceConstructibleRecordInsertSelect ()
218  {
219  auto adapted = Util::oral::AdaptPtr<NonInPlaceConstructibleRecord, OralFactory> (MakeDatabase ());
220  for (int i = 0; i < 3; ++i)
221  adapted->Insert ({ i, QString::number (i), 0 });
222 
223  const auto& list = adapted->Select ();
224  QCOMPARE (list, (QList<NonInPlaceConstructibleRecord> { { 0, "0", 0 }, { 1, "1", 0 }, { 2, "2", 0 } }));
225  }
226 
227  void OralTest::testComplexConstraintsRecordInsertSelectDefault ()
228  {
229  auto adapted = Util::oral::AdaptPtr<ComplexConstraintsRecord, OralFactory> (MakeDatabase ());
230 
231  adapted->Insert ({ 0, "first", "c1", 1, 2 });
232  QVERIFY_THROWS_EXCEPTION (oral::QueryException, adapted->Insert ({ 0, "second", "c1", 1, 2 }));
233  QVERIFY_THROWS_EXCEPTION (oral::QueryException, adapted->Insert ({ 0, "first", "c1", 1, 3 }));
234  adapted->Insert ({ 0, "second", "c2", 1, 3 });
235  QVERIFY_THROWS_EXCEPTION (oral::QueryException, adapted->Insert ({ 0, "first", "c1", 1, 3 }));
236 
237  const auto& list = adapted->Select ();
238  QCOMPARE (list, (QList<ComplexConstraintsRecord> { { 0, "first", "c1", 1, 2 }, { 0, "second", "c2", 1, 3 } }));
239  }
240 
241  void OralTest::testComplexConstraintsRecordInsertSelectIgnore ()
242  {
243  auto adapted = Util::oral::AdaptPtr<ComplexConstraintsRecord, OralFactory> (MakeDatabase ());
244 
245  adapted->Insert ({ 0, "first", "c1", 1, 2 }, lco::InsertAction::Ignore);
246  adapted->Insert ({ 0, "second", "c2", 1, 2 }, lco::InsertAction::Ignore);
247  adapted->Insert ({ 0, "first", "c3", 1, 3 }, lco::InsertAction::Ignore);
248  adapted->Insert ({ 0, "second", "c4", 1, 3 }, lco::InsertAction::Ignore);
249  adapted->Insert ({ 0, "first", "c5", 1, 3 }, lco::InsertAction::Ignore);
250 
251  const auto& list = adapted->Select ();
252  QCOMPARE (list, (QList<ComplexConstraintsRecord> { { 0, "first", "c1", 1, 2 }, { 0, "second", "c4", 1, 3 } }));
253  }
254 
255  void OralTest::testComplexConstraintsRecordInsertSelectReplace ()
256  {
257  auto adapted = Util::oral::AdaptPtr<ComplexConstraintsRecord, OralFactory> (MakeDatabase ());
258 
259  adapted->Insert ({ 0, "alice", "city1", 1, 2 });
260  adapted->Insert ({ 0, "bob", "city2", 1, 2 },
261  lco::InsertAction::Replace::Fields<&ComplexConstraintsRecord::Name_>);
262  QCOMPARE (adapted->Select (), (QList<ComplexConstraintsRecord> { { 0, "bob", "city1", 1, 2 } }));
263 
264  adapted->Insert ({ 0, "alice", "city3", 2, 3 });
265  QCOMPARE (adapted->Select (), (QList<ComplexConstraintsRecord> { { 0, "bob", "city1", 1, 2 }, { 0, "alice", "city3", 2, 3 } }));
266 
267  // cascading constraint violation: (0, "alice") ↦ (0, "bob") fails
268  QVERIFY_THROWS_EXCEPTION (oral::QueryException,
269  adapted->Insert ({ 1, "bob", "city4", 2, 3 },
270  lco::InsertAction::Replace::Fields<&ComplexConstraintsRecord::Name_, &ComplexConstraintsRecord::City_>));
271 
272  adapted->Insert ({ 1, "bob", "city4", 2, 3 },
273  lco::InsertAction::Replace::Fields<&ComplexConstraintsRecord::ID_, &ComplexConstraintsRecord::Name_, &ComplexConstraintsRecord::City_>);
274  QCOMPARE (adapted->Select (), (QList<ComplexConstraintsRecord> { { 0, "bob", "city1", 1, 2 }, { 1, "bob", "city4", 2, 3 } }));
275  }
276 
277  void OralTest::testConstrainedAutogenPKeyRecordInsertIgnore ()
278  {
279  using Rec = ConstrainedAutogenPKeyRecord;
280  auto adapted = Util::oral::AdaptPtr<Rec, OralFactory> (MakeDatabase ());
281 
282  QCOMPARE (adapted->Insert ({ .City_ = "c1", .Population_ = 100 }), 1);
283  QCOMPARE (adapted->Insert ({ .City_ = "c2", .Population_ = 200 }), 2);
284  QCOMPARE (adapted->Select (), (QList<Rec> { { 1, "c1", 100 }, { 2, "c2", 200 } }));
285 
286  QCOMPARE (adapted->Insert ({ .City_ = "c1", .Population_ = 300 }, lco::InsertAction::Ignore), std::optional<int> {});
287 
288  QCOMPARE (adapted->Select (), (QList<Rec> { { 1, "c1", 100 }, { 2, "c2", 200 } }));
289  }
290 
291  void OralTest::testConstrainedAutogenPKeyRecordInsertReplace ()
292  {
293  using Rec = ConstrainedAutogenPKeyRecord;
294  auto adapted = Util::oral::AdaptPtr<Rec, OralFactory> (MakeDatabase ());
295 
296  QCOMPARE (adapted->Insert ({ .City_ = "c1", .Population_ = 100 }), 1);
297  QCOMPARE (adapted->Insert ({ .City_ = "c2", .Population_ = 200 }), 2);
298  QCOMPARE (adapted->Select (), (QList<Rec> { { 1, "c1", 100 }, { 2, "c2", 200 } }));
299 
300  QVERIFY_THROWS_EXCEPTION (oral::QueryException, adapted->Insert ({ .City_ = "c1", .Population_ = 300 }));
301 
302  QCOMPARE (adapted->Insert ({ .City_ = "c1", .Population_ = 300 }, lco::InsertAction::Replace::Whole), 1);
303  QCOMPARE (adapted->Insert ({ .City_ = "c2", .Population_ = 400 }, lco::InsertAction::Replace::Whole), 2);
304  QCOMPARE (adapted->Select (), (QList<Rec> { { 1, "c1", 300 }, { 2, "c2", 400 } }));
305  }
306 
307  void OralTest::testOptionalFieldNullRoundTrip ()
308  {
309  using Rec = OptionalFieldRecord;
310  auto adapted = Util::oral::AdaptPtr<Rec, OralFactory> (MakeDatabase ());
311 
312  adapted->Insert ({ {}, "alice", std::nullopt, std::nullopt });
313  adapted->Insert ({ {}, "bob", std::nullopt, std::nullopt });
314 
315  const auto& list = adapted->Select ();
316  QCOMPARE (list.size (), 2);
317  QCOMPARE (list [0].NickName_, std::nullopt);
318  QCOMPARE (list [0].Extra_, std::nullopt);
319  QCOMPARE (list [1].NickName_, std::nullopt);
320  QCOMPARE (list [1].Extra_, std::nullopt);
321  }
322 
323  void OralTest::testOptionalFieldValueRoundTrip ()
324  {
325  using Rec = OptionalFieldRecord;
326  auto adapted = Util::oral::AdaptPtr<Rec, OralFactory> (MakeDatabase ());
327 
328  adapted->Insert ({ {}, "alice", "Al", QByteArray { "data1" } });
329  adapted->Insert ({ {}, "bob", std::nullopt, std::nullopt });
330 
331  const auto& list = adapted->Select ();
332  QCOMPARE (list.size (), 2);
333  QCOMPARE (list [0].NickName_, std::optional<QString> { "Al" });
334  QCOMPARE (list [0].Extra_, std::optional<QByteArray> { "data1" });
335  QCOMPARE (list [1].NickName_, std::nullopt);
336  QCOMPARE (list [1].Extra_, std::nullopt);
337  }
338 
339  void OralTest::testOptionalFieldUpdateNullToValue ()
340  {
341  using Rec = OptionalFieldRecord;
342  auto adapted = Util::oral::AdaptPtr<Rec, OralFactory> (MakeDatabase ());
343 
344  adapted->Insert ({ {}, "alice", std::nullopt, std::nullopt });
345  adapted->Update (sph::f<&Rec::NickName_> = QString { "Al" },
346  sph::f<&Rec::Name_> == QString { "alice" });
347 
348  const auto& list = adapted->Select ();
349  QCOMPARE (list.size (), 1);
350  QCOMPARE (list [0].NickName_, std::optional<QString> { "Al" });
351  QCOMPARE (list [0].Extra_, std::nullopt);
352  }
353 
354  void OralTest::testOptionalFieldUpdateValueToNull ()
355  {
356  using Rec = OptionalFieldRecord;
357  auto adapted = Util::oral::AdaptPtr<Rec, OralFactory> (MakeDatabase ());
358 
359  adapted->Insert ({ {}, "alice", "Al", QByteArray { "data" } });
360  adapted->Update (sph::f<&Rec::NickName_> = std::optional<QString> {},
361  sph::f<&Rec::Name_> == QString { "alice" });
362 
363  const auto& list = adapted->Select ();
364  QCOMPARE (list.size (), 1);
365  QCOMPARE (list [0].NickName_, std::nullopt);
366  QCOMPARE (list [0].Extra_, std::optional<QByteArray> { "data" });
367  }
368 }
369 }
std::string Name_
auto AsTuple() const
Definition: oraltest.cpp:23
static constexpr struct LC::Util::oral::InsertAction::IgnoreTag Ignore
QSqlDatabase MakeDatabase(const QString &name=":memory:")
Definition: common.h:56
ORAL_ADAPT_STRUCT(AutogenPKeyRecord, ID_, Value_) struct NoPKeyRecord
Definition: oraltest.cpp:29
static constexpr auto ClassName
Definition: oraltest.cpp:21
Typelist< Args... > Constraints
Definition: oraltypes.h:139
const QVariant Value_
Definition: plotitem.cpp:74
#define TOSTRING(n)
Definition: common.h:35
lco::PKey< int > ID_
Definition: oraltest.cpp:18