LeechCraft 0.6.70-18808-g3467692359
Modular cross-platform feature rich live environment.
Loading...
Searching...
No Matches
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
12QTEST_GUILESS_MAIN (LC::Util::OralTest)
13
14using 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
35struct 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
48ORAL_ADAPT_STRUCT (NoPKeyRecord,
49 ID_,
50 Value_)
51
52TOSTRING (NoPKeyRecord)
53
54struct 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
76ORAL_ADAPT_STRUCT (NonInPlaceConstructibleRecord,
77 ID_,
78 Value_)
79
80TOSTRING (NonInPlaceConstructibleRecord)
81
82struct 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
96ORAL_ADAPT_STRUCT (ConstrainedAutogenPKeyRecord,
97 ID_,
98 City_,
99 Population_)
100
101TOSTRING (ConstrainedAutogenPKeyRecord)
102
103struct 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
118ORAL_ADAPT_STRUCT (OptionalFieldRecord,
119 ID_,
120 Name_,
121 NickName_,
122 Extra_)
123
124TOSTRING (OptionalFieldRecord)
125
126struct 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
141 using Constraints = lco::Constraints<
144 >;
145};
146
147ORAL_ADAPT_STRUCT (ComplexConstraintsRecord,
148 ID_,
149 Name_,
150 City_,
151 Age_,
152 Weight_)
153
154TOSTRING (ComplexConstraintsRecord)
155
156namespace LC
157{
158namespace Util
159{
160 namespace sph = oral::sph;
161
162 void OralTest::testNestedIndirect ()
163 {
165
167 static_assert (std::is_same_v<std::decay_t<decltype (*prid)>, int>);
168 QCOMPARE (prid, 1);
169 QCOMPARE (*prid, 1);
170
172 QCOMPARE (purid, 1);
173 static_assert (std::is_same_v<std::decay_t<decltype (*purid)>, int>);
174 }
175
176 void OralTest::testAutoPKeyRecordInsertSelect ()
177 {
180 const auto& list = adapted->Select ();
181 QCOMPARE (list, (QList<AutogenPKeyRecord> { { 1, "0" }, { 2, "1" }, { 3, "2" } }));
182 }
183
184 void OralTest::testAutoPKeyRecordInsertRvalueReturnsPKey ()
185 {
187
188 QList<int> ids;
189 for (int i = 0; i < 3; ++i)
190 ids << adapted->Insert ({ 0, QString::number (i) });
191
192 QCOMPARE (ids, (QList<int> { 1, 2, 3 }));
193 }
194
195 void OralTest::testAutoPKeyRecordInsertConstLvalueReturnsPKey ()
196 {
198
200 for (int i = 0; i < 3; ++i)
201 records.push_back ({ 0, QString::number (i) });
202
203 QList<int> ids;
204 for (const auto& record : records)
205 ids << adapted->Insert (record);
206
207 QCOMPARE (ids, (QList<int> { 1, 2, 3 }));
208 }
209
210 void OralTest::testAutoPKeyRecordInsertSetsPKey ()
211 {
213
215 for (int i = 0; i < 3; ++i)
216 records.push_back ({ 0, QString::number (i) });
217
218 for (auto& record : records)
219 adapted->Insert (record);
220
221 QCOMPARE (records, (QList<AutogenPKeyRecord> { { 1, "0" }, { 2, "1" }, { 3, "2" } }));
222 }
223
224 void OralTest::testNoPKeyRecordInsertSelect ()
225 {
226 auto adapted = PrepareRecords<NoPKeyRecord> (MakeDatabase ());
227 const auto& list = adapted->Select ();
228 QCOMPARE (list, (QList<NoPKeyRecord> { { 0, "0" }, { 1, "1" }, { 2, "2" } }));
229 }
230
231 void OralTest::testNonInPlaceConstructibleRecordInsertSelect ()
232 {
234 for (int i = 0; i < 3; ++i)
235 adapted->Insert ({ i, QString::number (i), 0 });
236
237 const auto& list = adapted->Select ();
238 QCOMPARE (list, (QList<NonInPlaceConstructibleRecord> { { 0, "0", 0 }, { 1, "1", 0 }, { 2, "2", 0 } }));
239 }
240
241 void OralTest::testComplexConstraintsRecordInsertSelectDefault ()
242 {
244
245 adapted->Insert ({ 0, "first", "c1", 1, 2 });
246 QVERIFY_THROWS_EXCEPTION (oral::QueryException, adapted->Insert ({ 0, "second", "c1", 1, 2 }));
247 QVERIFY_THROWS_EXCEPTION (oral::QueryException, adapted->Insert ({ 0, "first", "c1", 1, 3 }));
248 adapted->Insert ({ 0, "second", "c2", 1, 3 });
249 QVERIFY_THROWS_EXCEPTION (oral::QueryException, adapted->Insert ({ 0, "first", "c1", 1, 3 }));
250
251 const auto& list = adapted->Select ();
252 QCOMPARE (list, (QList<ComplexConstraintsRecord> { { 0, "first", "c1", 1, 2 }, { 0, "second", "c2", 1, 3 } }));
253 }
254
255 void OralTest::testComplexConstraintsRecordInsertSelectIgnore ()
256 {
258
259 adapted->Insert ({ 0, "first", "c1", 1, 2 }, lco::InsertAction::Ignore);
260 adapted->Insert ({ 0, "second", "c2", 1, 2 }, lco::InsertAction::Ignore);
261 adapted->Insert ({ 0, "first", "c3", 1, 3 }, lco::InsertAction::Ignore);
262 adapted->Insert ({ 0, "second", "c4", 1, 3 }, lco::InsertAction::Ignore);
263 adapted->Insert ({ 0, "first", "c5", 1, 3 }, lco::InsertAction::Ignore);
264
265 const auto& list = adapted->Select ();
266 QCOMPARE (list, (QList<ComplexConstraintsRecord> { { 0, "first", "c1", 1, 2 }, { 0, "second", "c4", 1, 3 } }));
267 }
268
269 void OralTest::testComplexConstraintsRecordInsertSelectReplace ()
270 {
272
273 adapted->Insert ({ 0, "alice", "city1", 1, 2 });
274 adapted->Insert ({ 0, "bob", "city2", 1, 2 },
276 QCOMPARE (adapted->Select (), (QList<ComplexConstraintsRecord> { { 0, "bob", "city1", 1, 2 } }));
277
278 adapted->Insert ({ 0, "alice", "city3", 2, 3 });
279 QCOMPARE (adapted->Select (), (QList<ComplexConstraintsRecord> { { 0, "bob", "city1", 1, 2 }, { 0, "alice", "city3", 2, 3 } }));
280
281 // cascading constraint violation: (0, "alice") ↦ (0, "bob") fails
282 QVERIFY_THROWS_EXCEPTION (oral::QueryException,
283 adapted->Insert ({ 1, "bob", "city4", 2, 3 },
285
286 adapted->Insert ({ 1, "bob", "city4", 2, 3 },
288 QCOMPARE (adapted->Select (), (QList<ComplexConstraintsRecord> { { 0, "bob", "city1", 1, 2 }, { 1, "bob", "city4", 2, 3 } }));
289 }
290
291 void OralTest::testConstrainedAutogenPKeyRecordInsertIgnore ()
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 QCOMPARE (adapted->Insert ({ .City_ = "c1", .Population_ = 300 }, lco::InsertAction::Ignore), std::optional<int> {});
301
302 QCOMPARE (adapted->Select (), (QList<Rec> { { 1, "c1", 100 }, { 2, "c2", 200 } }));
303 }
304
305 void OralTest::testConstrainedAutogenPKeyRecordInsertReplace ()
306 {
307 using Rec = ConstrainedAutogenPKeyRecord;
308 auto adapted = Util::oral::AdaptPtr<Rec, OralFactory> (MakeDatabase ());
309
310 QCOMPARE (adapted->Insert ({ .City_ = "c1", .Population_ = 100 }), 1);
311 QCOMPARE (adapted->Insert ({ .City_ = "c2", .Population_ = 200 }), 2);
312 QCOMPARE (adapted->Select (), (QList<Rec> { { 1, "c1", 100 }, { 2, "c2", 200 } }));
313
314 QVERIFY_THROWS_EXCEPTION (oral::QueryException, adapted->Insert ({ .City_ = "c1", .Population_ = 300 }));
315
316 QCOMPARE (adapted->Insert ({ .City_ = "c1", .Population_ = 300 }, lco::InsertAction::Replace::Whole), 1);
317 QCOMPARE (adapted->Insert ({ .City_ = "c2", .Population_ = 400 }, lco::InsertAction::Replace::Whole), 2);
318 QCOMPARE (adapted->Select (), (QList<Rec> { { 1, "c1", 300 }, { 2, "c2", 400 } }));
319 }
320
321 void OralTest::testOptionalFieldNullRoundTrip ()
322 {
323 using Rec = OptionalFieldRecord;
324 auto adapted = Util::oral::AdaptPtr<Rec, OralFactory> (MakeDatabase ());
325
326 adapted->Insert ({ {}, "alice", std::nullopt, std::nullopt });
327 adapted->Insert ({ {}, "bob", std::nullopt, std::nullopt });
328
329 const auto& list = adapted->Select ();
330 QCOMPARE (list.size (), 2);
331 QCOMPARE (list [0].NickName_, std::nullopt);
332 QCOMPARE (list [0].Extra_, std::nullopt);
333 QCOMPARE (list [1].NickName_, std::nullopt);
334 QCOMPARE (list [1].Extra_, std::nullopt);
335 }
336
337 void OralTest::testOptionalFieldValueRoundTrip ()
338 {
339 using Rec = OptionalFieldRecord;
340 auto adapted = Util::oral::AdaptPtr<Rec, OralFactory> (MakeDatabase ());
341
342 adapted->Insert ({ {}, "alice", "Al", QByteArray { "data1" } });
343 adapted->Insert ({ {}, "bob", std::nullopt, std::nullopt });
344
345 const auto& list = adapted->Select ();
346 QCOMPARE (list.size (), 2);
347 QCOMPARE (list [0].NickName_, std::optional<QString> { "Al" });
348 QCOMPARE (list [0].Extra_, std::optional<QByteArray> { "data1" });
349 QCOMPARE (list [1].NickName_, std::nullopt);
350 QCOMPARE (list [1].Extra_, std::nullopt);
351 }
352
353 void OralTest::testOptionalFieldUpdateNullToValue ()
354 {
355 using Rec = OptionalFieldRecord;
356 auto adapted = Util::oral::AdaptPtr<Rec, OralFactory> (MakeDatabase ());
357
358 adapted->Insert ({ {}, "alice", std::nullopt, std::nullopt });
359 adapted->Update (sph::f<&Rec::NickName_> = QString { "Al" },
360 sph::f<&Rec::Name_> == QString { "alice" });
361
362 const auto& list = adapted->Select ();
363 QCOMPARE (list.size (), 1);
364 QCOMPARE (list [0].NickName_, std::optional<QString> { "Al" });
365 QCOMPARE (list [0].Extra_, std::nullopt);
366 }
367
368 void OralTest::testOptionalFieldUpdateValueToNull ()
369 {
370 using Rec = OptionalFieldRecord;
371 auto adapted = Util::oral::AdaptPtr<Rec, OralFactory> (MakeDatabase ());
372
373 adapted->Insert ({ {}, "alice", "Al", QByteArray { "data" } });
374 adapted->Update (sph::f<&Rec::NickName_> = std::optional<QString> {},
375 sph::f<&Rec::Name_> == QString { "alice" });
376
377 const auto& list = adapted->Select ();
378 QCOMPARE (list.size (), 1);
379 QCOMPARE (list [0].NickName_, std::nullopt);
380 QCOMPARE (list [0].Extra_, std::optional<QByteArray> { "data" });
381 }
382}
383}
#define TOSTRING(n)
Definition common.h:35
constexpr auto FieldNames
Definition oral.h:136
constexpr bool IsForeignKeyRecursive()
Definition oral.h:333
Typelist< Args... > Constraints
Definition oraltypes.h:185
ObjectInfo_ptr< T > AdaptPtr(const QSqlDatabase &db)
Definition oral.h:1832
auto PrepareRecords(QSqlDatabase db, int count=3)
Definition common.h:69
QSqlDatabase MakeDatabase(const QString &name=":memory:")
Definition common.h:56
Definition constants.h:15
#define ORAL_ADAPT_STRUCT(sname,...)
Definition oral.h:44
auto AsTuple() const
Definition oraltest.cpp:23
static constexpr auto ClassName
Definition oraltest.cpp:21
lco::PKey< int > ID_
Definition oraltest.cpp:18
static constexpr struct LC::Util::oral::InsertAction::Replace::WholeType Whole
static constexpr FieldsType< Ptrs... > Fields
Definition oraltypes.h:207
static constexpr struct LC::Util::oral::InsertAction::IgnoreTag Ignore