LeechCraft 0.6.70-18450-gabe19ee3b0
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::testAutoPKeyRecordInsertSelect ()
163 {
166 const auto& list = adapted->Select ();
167 QCOMPARE (list, (QList<AutogenPKeyRecord> { { 1, "0" }, { 2, "1" }, { 3, "2" } }));
168 }
169
170 void OralTest::testAutoPKeyRecordInsertRvalueReturnsPKey ()
171 {
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 {
184
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 {
199
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 {
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 {
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 {
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 {
258
259 adapted->Insert ({ 0, "alice", "city1", 1, 2 });
260 adapted->Insert ({ 0, "bob", "city2", 1, 2 },
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 },
271
272 adapted->Insert ({ 1, "bob", "city4", 2, 3 },
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}
#define TOSTRING(n)
Definition common.h:35
constexpr auto FieldNames
Definition oral.h:136
Typelist< Args... > Constraints
Definition oraltypes.h:139
ObjectInfo_ptr< T > AdaptPtr(const QSqlDatabase &db)
Definition oral.h:1836
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:173
static constexpr struct LC::Util::oral::InsertAction::IgnoreTag Ignore