1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com> |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtCore module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include <QStringView> |
30 | #include <QString> |
31 | #include <QChar> |
32 | #include <QStringRef> |
33 | |
34 | #include <QTest> |
35 | |
36 | #include <string> |
37 | |
38 | template <typename T> |
39 | using CanConvert = std::is_convertible<T, QStringView>; |
40 | |
41 | Q_STATIC_ASSERT(!CanConvert<QLatin1String>::value); |
42 | Q_STATIC_ASSERT(!CanConvert<const char*>::value); |
43 | Q_STATIC_ASSERT(!CanConvert<QByteArray>::value); |
44 | |
45 | // QStringView qchar_does_not_compile() { return QStringView(QChar('a')); } |
46 | // QStringView qlatin1string_does_not_compile() { return QStringView(QLatin1String("a")); } |
47 | // QStringView const_char_star_does_not_compile() { return QStringView("a"); } |
48 | // QStringView qbytearray_does_not_compile() { return QStringView(QByteArray("a")); } |
49 | |
50 | // |
51 | // QChar |
52 | // |
53 | |
54 | Q_STATIC_ASSERT(!CanConvert<QChar>::value); |
55 | |
56 | Q_STATIC_ASSERT(CanConvert<QChar[123]>::value); |
57 | |
58 | Q_STATIC_ASSERT(CanConvert< QString >::value); |
59 | Q_STATIC_ASSERT(CanConvert<const QString >::value); |
60 | Q_STATIC_ASSERT(CanConvert< QString&>::value); |
61 | Q_STATIC_ASSERT(CanConvert<const QString&>::value); |
62 | |
63 | Q_STATIC_ASSERT(CanConvert< QStringRef >::value); |
64 | Q_STATIC_ASSERT(CanConvert<const QStringRef >::value); |
65 | Q_STATIC_ASSERT(CanConvert< QStringRef&>::value); |
66 | Q_STATIC_ASSERT(CanConvert<const QStringRef&>::value); |
67 | |
68 | |
69 | // |
70 | // ushort |
71 | // |
72 | |
73 | Q_STATIC_ASSERT(!CanConvert<ushort>::value); |
74 | |
75 | Q_STATIC_ASSERT(CanConvert<ushort[123]>::value); |
76 | |
77 | Q_STATIC_ASSERT(CanConvert< ushort*>::value); |
78 | Q_STATIC_ASSERT(CanConvert<const ushort*>::value); |
79 | |
80 | |
81 | // |
82 | // char16_t |
83 | // |
84 | |
85 | #if defined(Q_COMPILER_UNICODE_STRINGS) |
86 | |
87 | Q_STATIC_ASSERT(!CanConvert<char16_t>::value); |
88 | |
89 | Q_STATIC_ASSERT(CanConvert< char16_t*>::value); |
90 | Q_STATIC_ASSERT(CanConvert<const char16_t*>::value); |
91 | |
92 | #endif |
93 | |
94 | #if defined(Q_STDLIB_UNICODE_STRINGS) |
95 | |
96 | Q_STATIC_ASSERT(CanConvert< std::u16string >::value); |
97 | Q_STATIC_ASSERT(CanConvert<const std::u16string >::value); |
98 | Q_STATIC_ASSERT(CanConvert< std::u16string&>::value); |
99 | Q_STATIC_ASSERT(CanConvert<const std::u16string&>::value); |
100 | |
101 | #endif |
102 | |
103 | |
104 | // |
105 | // wchar_t |
106 | // |
107 | |
108 | Q_CONSTEXPR bool CanConvertFromWCharT = |
109 | #ifdef Q_OS_WIN |
110 | true |
111 | #else |
112 | false |
113 | #endif |
114 | ; |
115 | |
116 | Q_STATIC_ASSERT(!CanConvert<wchar_t>::value); |
117 | |
118 | Q_STATIC_ASSERT(CanConvert< wchar_t*>::value == CanConvertFromWCharT); |
119 | Q_STATIC_ASSERT(CanConvert<const wchar_t*>::value == CanConvertFromWCharT); |
120 | |
121 | Q_STATIC_ASSERT(CanConvert< std::wstring >::value == CanConvertFromWCharT); |
122 | Q_STATIC_ASSERT(CanConvert<const std::wstring >::value == CanConvertFromWCharT); |
123 | Q_STATIC_ASSERT(CanConvert< std::wstring&>::value == CanConvertFromWCharT); |
124 | Q_STATIC_ASSERT(CanConvert<const std::wstring&>::value == CanConvertFromWCharT); |
125 | |
126 | |
127 | class tst_QStringView : public QObject |
128 | { |
129 | Q_OBJECT |
130 | |
131 | private Q_SLOTS: |
132 | void constExpr() const; |
133 | void basics() const; |
134 | void literals() const; |
135 | void at() const; |
136 | |
137 | void arg() const; |
138 | |
139 | void fromQString() const; |
140 | void fromQStringRef() const; |
141 | |
142 | void fromQCharStar() const |
143 | { |
144 | const QChar str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', 0 }; |
145 | fromLiteral(arg: str); |
146 | } |
147 | |
148 | void fromUShortStar() const |
149 | { |
150 | const ushort str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', 0 }; |
151 | fromLiteral(arg: str); |
152 | } |
153 | |
154 | void fromChar16TStar() const |
155 | { |
156 | #if defined(Q_COMPILER_UNICODE_STRINGS) |
157 | fromLiteral(arg: u"Hello, World!" ); |
158 | #else |
159 | QSKIP("This test requires C++11 char16_t support enabled in the compiler" ); |
160 | #endif |
161 | } |
162 | |
163 | void fromWCharTStar() const |
164 | { |
165 | #ifdef Q_OS_WIN |
166 | fromLiteral(L"Hello, World!" ); |
167 | #else |
168 | QSKIP("This is a Windows-only test" ); |
169 | #endif |
170 | } |
171 | |
172 | void fromQCharRange() const |
173 | { |
174 | const QChar str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; |
175 | fromRange(first: std::begin(arr: str), last: std::end(arr: str)); |
176 | } |
177 | |
178 | void fromUShortRange() const |
179 | { |
180 | const ushort str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; |
181 | fromRange(first: std::begin(arr: str), last: std::end(arr: str)); |
182 | } |
183 | |
184 | void fromChar16TRange() const |
185 | { |
186 | #if defined(Q_COMPILER_UNICODE_STRINGS) |
187 | const char16_t str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; |
188 | fromRange(first: std::begin(arr: str), last: std::end(arr: str)); |
189 | #else |
190 | QSKIP("This test requires C++11 char16_t support enabled in the compiler" ); |
191 | #endif |
192 | } |
193 | |
194 | void fromWCharTRange() const |
195 | { |
196 | #ifdef Q_OS_WIN |
197 | const wchar_t str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; |
198 | fromRange(std::begin(str), std::end(str)); |
199 | #else |
200 | QSKIP("This is a Windows-only test" ); |
201 | #endif |
202 | } |
203 | |
204 | // std::basic_string |
205 | void fromStdStringWCharT() const |
206 | { |
207 | #ifdef Q_OS_WIN |
208 | fromStdString<wchar_t>(); |
209 | #else |
210 | QSKIP("This is a Windows-only test" ); |
211 | #endif |
212 | } |
213 | void fromStdStringChar16T() const |
214 | { |
215 | #ifdef Q_STDLIB_UNICODE_STRINGS |
216 | fromStdString<char16_t>(); |
217 | #else |
218 | QSKIP("This test requires C++11 char16_t support enabled in compiler & stdlib" ); |
219 | #endif |
220 | } |
221 | |
222 | void comparison(); |
223 | |
224 | void overloadResolution(); |
225 | |
226 | private: |
227 | template <typename String> |
228 | void conversion_tests(String arg) const; |
229 | template <typename Char> |
230 | void fromLiteral(const Char *arg) const; |
231 | template <typename Char> |
232 | void fromRange(const Char *first, const Char *last) const; |
233 | template <typename Char, typename Container> |
234 | void fromContainer() const; |
235 | template <typename Char> |
236 | void fromStdString() const { fromContainer<Char, std::basic_string<Char> >(); } |
237 | }; |
238 | |
239 | void tst_QStringView::constExpr() const |
240 | { |
241 | // compile-time checks |
242 | #ifdef Q_COMPILER_CONSTEXPR |
243 | { |
244 | constexpr QStringView sv; |
245 | Q_STATIC_ASSERT(sv.size() == 0); |
246 | Q_STATIC_ASSERT(sv.isNull()); |
247 | Q_STATIC_ASSERT(sv.empty()); |
248 | Q_STATIC_ASSERT(sv.isEmpty()); |
249 | Q_STATIC_ASSERT(sv.utf16() == nullptr); |
250 | |
251 | constexpr QStringView sv2(sv.utf16(), sv.utf16() + sv.size()); |
252 | Q_STATIC_ASSERT(sv2.isNull()); |
253 | Q_STATIC_ASSERT(sv2.empty()); |
254 | } |
255 | { |
256 | constexpr QStringView sv = nullptr; |
257 | Q_STATIC_ASSERT(sv.size() == 0); |
258 | Q_STATIC_ASSERT(sv.isNull()); |
259 | Q_STATIC_ASSERT(sv.empty()); |
260 | Q_STATIC_ASSERT(sv.isEmpty()); |
261 | Q_STATIC_ASSERT(sv.utf16() == nullptr); |
262 | } |
263 | { |
264 | constexpr QStringView sv = u"" ; |
265 | Q_STATIC_ASSERT(sv.size() == 0); |
266 | Q_STATIC_ASSERT(!sv.isNull()); |
267 | Q_STATIC_ASSERT(sv.empty()); |
268 | Q_STATIC_ASSERT(sv.isEmpty()); |
269 | Q_STATIC_ASSERT(sv.utf16() != nullptr); |
270 | |
271 | constexpr QStringView sv2(sv.utf16(), sv.utf16() + sv.size()); |
272 | Q_STATIC_ASSERT(!sv2.isNull()); |
273 | Q_STATIC_ASSERT(sv2.empty()); |
274 | } |
275 | { |
276 | constexpr QStringView sv = u"Hello" ; |
277 | Q_STATIC_ASSERT(sv.size() == 5); |
278 | Q_STATIC_ASSERT(!sv.empty()); |
279 | Q_STATIC_ASSERT(!sv.isEmpty()); |
280 | Q_STATIC_ASSERT(!sv.isNull()); |
281 | Q_STATIC_ASSERT(*sv.utf16() == 'H'); |
282 | Q_STATIC_ASSERT(sv[0] == QLatin1Char('H')); |
283 | Q_STATIC_ASSERT(sv.at(0) == QLatin1Char('H')); |
284 | Q_STATIC_ASSERT(sv.front() == QLatin1Char('H')); |
285 | Q_STATIC_ASSERT(sv.first() == QLatin1Char('H')); |
286 | Q_STATIC_ASSERT(sv[4] == QLatin1Char('o')); |
287 | Q_STATIC_ASSERT(sv.at(4) == QLatin1Char('o')); |
288 | Q_STATIC_ASSERT(sv.back() == QLatin1Char('o')); |
289 | Q_STATIC_ASSERT(sv.last() == QLatin1Char('o')); |
290 | |
291 | constexpr QStringView sv2(sv.utf16(), sv.utf16() + sv.size()); |
292 | Q_STATIC_ASSERT(!sv2.isNull()); |
293 | Q_STATIC_ASSERT(!sv2.empty()); |
294 | Q_STATIC_ASSERT(sv2.size() == 5); |
295 | } |
296 | #if !defined(Q_OS_WIN) || defined(Q_COMPILER_UNICODE_STRINGS) |
297 | { |
298 | Q_STATIC_ASSERT(QStringView(u"Hello" ).size() == 5); |
299 | constexpr QStringView sv = u"Hello" ; |
300 | Q_STATIC_ASSERT(sv.size() == 5); |
301 | Q_STATIC_ASSERT(!sv.empty()); |
302 | Q_STATIC_ASSERT(!sv.isEmpty()); |
303 | Q_STATIC_ASSERT(!sv.isNull()); |
304 | Q_STATIC_ASSERT(*sv.utf16() == 'H'); |
305 | Q_STATIC_ASSERT(sv[0] == QLatin1Char('H')); |
306 | Q_STATIC_ASSERT(sv.at(0) == QLatin1Char('H')); |
307 | Q_STATIC_ASSERT(sv.front() == QLatin1Char('H')); |
308 | Q_STATIC_ASSERT(sv.first() == QLatin1Char('H')); |
309 | Q_STATIC_ASSERT(sv[4] == QLatin1Char('o')); |
310 | Q_STATIC_ASSERT(sv.at(4) == QLatin1Char('o')); |
311 | Q_STATIC_ASSERT(sv.back() == QLatin1Char('o')); |
312 | Q_STATIC_ASSERT(sv.last() == QLatin1Char('o')); |
313 | |
314 | constexpr QStringView sv2(sv.utf16(), sv.utf16() + sv.size()); |
315 | Q_STATIC_ASSERT(!sv2.isNull()); |
316 | Q_STATIC_ASSERT(!sv2.empty()); |
317 | Q_STATIC_ASSERT(sv2.size() == 5); |
318 | |
319 | constexpr char16_t *null = nullptr; |
320 | constexpr QStringView sv3(null); |
321 | Q_STATIC_ASSERT(sv3.isNull()); |
322 | Q_STATIC_ASSERT(sv3.isEmpty()); |
323 | Q_STATIC_ASSERT(sv3.size() == 0); |
324 | } |
325 | #else // storage_type is wchar_t |
326 | { |
327 | Q_STATIC_ASSERT(QStringView(L"Hello" ).size() == 5); |
328 | constexpr QStringView sv = L"Hello" ; |
329 | Q_STATIC_ASSERT(sv.size() == 5); |
330 | Q_STATIC_ASSERT(!sv.empty()); |
331 | Q_STATIC_ASSERT(!sv.isEmpty()); |
332 | Q_STATIC_ASSERT(!sv.isNull()); |
333 | Q_STATIC_ASSERT(*sv.utf16() == 'H'); |
334 | Q_STATIC_ASSERT(sv[0] == QLatin1Char('H')); |
335 | Q_STATIC_ASSERT(sv.at(0) == QLatin1Char('H')); |
336 | Q_STATIC_ASSERT(sv.front() == QLatin1Char('H')); |
337 | Q_STATIC_ASSERT(sv.first() == QLatin1Char('H')); |
338 | Q_STATIC_ASSERT(sv[4] == QLatin1Char('o')); |
339 | Q_STATIC_ASSERT(sv.at(4) == QLatin1Char('o')); |
340 | Q_STATIC_ASSERT(sv.back() == QLatin1Char('o')); |
341 | Q_STATIC_ASSERT(sv.last() == QLatin1Char('o')); |
342 | |
343 | constexpr QStringView sv2(sv.utf16(), sv.utf16() + sv.size()); |
344 | Q_STATIC_ASSERT(!sv2.isNull()); |
345 | Q_STATIC_ASSERT(!sv2.empty()); |
346 | Q_STATIC_ASSERT(sv2.size() == 5); |
347 | |
348 | constexpr wchar_t *null = nullptr; |
349 | constexpr QStringView sv3(null); |
350 | Q_STATIC_ASSERT(sv3.isNull()); |
351 | Q_STATIC_ASSERT(sv3.isEmpty()); |
352 | Q_STATIC_ASSERT(sv3.size() == 0); |
353 | } |
354 | #endif |
355 | #endif |
356 | } |
357 | |
358 | void tst_QStringView::basics() const |
359 | { |
360 | QStringView sv1; |
361 | |
362 | // a default-constructed QStringView is null: |
363 | QVERIFY(sv1.isNull()); |
364 | // which implies it's empty(); |
365 | QVERIFY(sv1.isEmpty()); |
366 | |
367 | QStringView sv2; |
368 | |
369 | QVERIFY(sv2 == sv1); |
370 | QVERIFY(!(sv2 != sv1)); |
371 | } |
372 | |
373 | void tst_QStringView::literals() const |
374 | { |
375 | #if !defined(Q_OS_WIN) || defined(Q_COMPILER_UNICODE_STRINGS) |
376 | const char16_t hello[] = u"Hello" ; |
377 | const char16_t longhello[] = |
378 | u"Hello World. This is a much longer message, to exercise qustrlen." ; |
379 | const char16_t withnull[] = u"a\0zzz" ; |
380 | #else // storage_type is wchar_t |
381 | const wchar_t hello[] = L"Hello" ; |
382 | const wchar_t longhello[] = |
383 | L"Hello World. This is a much longer message, to exercise qustrlen." ; |
384 | const wchar_t withnull[] = L"a\0zzz" ; |
385 | #endif |
386 | Q_STATIC_ASSERT(sizeof(longhello) >= 16); |
387 | |
388 | QCOMPARE(QStringView(hello).size(), 5); |
389 | QCOMPARE(QStringView(hello + 0).size(), 5); // forces decay to pointer |
390 | QStringView sv = hello; |
391 | QCOMPARE(sv.size(), 5); |
392 | QVERIFY(!sv.empty()); |
393 | QVERIFY(!sv.isEmpty()); |
394 | QVERIFY(!sv.isNull()); |
395 | QCOMPARE(*sv.utf16(), 'H'); |
396 | QCOMPARE(sv[0], QLatin1Char('H')); |
397 | QCOMPARE(sv.at(0), QLatin1Char('H')); |
398 | QCOMPARE(sv.front(), QLatin1Char('H')); |
399 | QCOMPARE(sv.first(), QLatin1Char('H')); |
400 | QCOMPARE(sv[4], QLatin1Char('o')); |
401 | QCOMPARE(sv.at(4), QLatin1Char('o')); |
402 | QCOMPARE(sv.back(), QLatin1Char('o')); |
403 | QCOMPARE(sv.last(), QLatin1Char('o')); |
404 | |
405 | QStringView sv2(sv.utf16(), sv.utf16() + sv.size()); |
406 | QVERIFY(!sv2.isNull()); |
407 | QVERIFY(!sv2.empty()); |
408 | QCOMPARE(sv2.size(), 5); |
409 | |
410 | QStringView sv3(longhello); |
411 | QCOMPARE(size_t(sv3.size()), sizeof(longhello)/sizeof(longhello[0]) - 1); |
412 | QCOMPARE(sv3.last(), QLatin1Char('.')); |
413 | sv3 = longhello; |
414 | QCOMPARE(size_t(sv3.size()), sizeof(longhello)/sizeof(longhello[0]) - 1); |
415 | |
416 | for (int i = 0; i < sv3.size(); ++i) { |
417 | QStringView sv4(longhello + i); |
418 | QCOMPARE(size_t(sv4.size()), sizeof(longhello)/sizeof(longhello[0]) - 1 - i); |
419 | QCOMPARE(sv4.last(), QLatin1Char('.')); |
420 | sv4 = longhello + i; |
421 | QCOMPARE(size_t(sv4.size()), sizeof(longhello)/sizeof(longhello[0]) - 1 - i); |
422 | } |
423 | |
424 | // these are different results |
425 | QCOMPARE(size_t(QStringView(withnull).size()), sizeof(withnull)/sizeof(withnull[0]) - 1); |
426 | QCOMPARE(QStringView(withnull + 0).size(), 1); |
427 | } |
428 | |
429 | void tst_QStringView::at() const |
430 | { |
431 | QString hello("Hello" ); |
432 | QStringView sv(hello); |
433 | QCOMPARE(sv.at(0), QChar('H')); QCOMPARE(sv[0], QChar('H')); |
434 | QCOMPARE(sv.at(1), QChar('e')); QCOMPARE(sv[1], QChar('e')); |
435 | QCOMPARE(sv.at(2), QChar('l')); QCOMPARE(sv[2], QChar('l')); |
436 | QCOMPARE(sv.at(3), QChar('l')); QCOMPARE(sv[3], QChar('l')); |
437 | QCOMPARE(sv.at(4), QChar('o')); QCOMPARE(sv[4], QChar('o')); |
438 | } |
439 | |
440 | void tst_QStringView::arg() const |
441 | { |
442 | #define CHECK1(pattern, arg1, expected) \ |
443 | do { \ |
444 | auto p = QStringView(u"" pattern); \ |
445 | QCOMPARE(p.arg(QLatin1String(arg1)), expected); \ |
446 | QCOMPARE(p.arg(u"" arg1), expected); \ |
447 | QCOMPARE(p.arg(QStringLiteral(arg1)), expected); \ |
448 | QCOMPARE(p.arg(QString(QLatin1String(arg1))), expected); \ |
449 | } while (false) \ |
450 | /*end*/ |
451 | #define CHECK2(pattern, arg1, arg2, expected) \ |
452 | do { \ |
453 | auto p = QStringView(u"" pattern); \ |
454 | QCOMPARE(p.arg(QLatin1String(arg1), QLatin1String(arg2)), expected); \ |
455 | QCOMPARE(p.arg(u"" arg1, QLatin1String(arg2)), expected); \ |
456 | QCOMPARE(p.arg(QLatin1String(arg1), u"" arg2), expected); \ |
457 | QCOMPARE(p.arg(u"" arg1, u"" arg2), expected); \ |
458 | } while (false) \ |
459 | /*end*/ |
460 | |
461 | CHECK1("" , "World" , "" ); |
462 | CHECK1("%1" , "World" , "World" ); |
463 | CHECK1("!%1?" , "World" , "!World?" ); |
464 | CHECK1("%1%1" , "World" , "WorldWorld" ); |
465 | CHECK1("%1%2" , "World" , "World%2" ); |
466 | CHECK1("%2%1" , "World" , "%2World" ); |
467 | |
468 | CHECK2("" , "Hello" , "World" , "" ); |
469 | CHECK2("%1" , "Hello" , "World" , "Hello" ); |
470 | CHECK2("!%1, %2?" , "Hello" , "World" , "!Hello, World?" ); |
471 | CHECK2("%1%1" , "Hello" , "World" , "HelloHello" ); |
472 | CHECK2("%1%2" , "Hello" , "World" , "HelloWorld" ); |
473 | CHECK2("%2%1" , "Hello" , "World" , "WorldHello" ); |
474 | |
475 | #undef CHECK2 |
476 | #undef CHECK1 |
477 | |
478 | QCOMPARE(QStringView(u" %2 %2 %1 %3 " ).arg(QLatin1Char('c'), QChar::CarriageReturn, u'C'), " \r \r c C " ); |
479 | } |
480 | |
481 | void tst_QStringView::fromQString() const |
482 | { |
483 | QString null; |
484 | QString empty = "" ; |
485 | |
486 | QVERIFY( QStringView(null).isNull()); |
487 | QVERIFY( QStringView(null).isEmpty()); |
488 | QVERIFY( QStringView(empty).isEmpty()); |
489 | QVERIFY(!QStringView(empty).isNull()); |
490 | |
491 | conversion_tests(string: QString("Hello World!" )); |
492 | } |
493 | |
494 | void tst_QStringView::fromQStringRef() const |
495 | { |
496 | QStringRef null; |
497 | QString emptyS = "" ; |
498 | QStringRef empty(&emptyS); |
499 | |
500 | QVERIFY( QStringView(null).isNull()); |
501 | QVERIFY( QStringView(null).isEmpty()); |
502 | QVERIFY( QStringView(empty).isEmpty()); |
503 | QVERIFY(!QStringView(empty).isNull()); |
504 | |
505 | conversion_tests(string: QString("Hello World!" ).midRef(position: 6)); |
506 | } |
507 | |
508 | template <typename Char> |
509 | void tst_QStringView::fromLiteral(const Char *arg) const |
510 | { |
511 | const Char *null = nullptr; |
512 | const Char empty[] = { 0 }; |
513 | |
514 | QCOMPARE(QStringView(null).size(), qsizetype(0)); |
515 | QCOMPARE(QStringView(null).data(), nullptr); |
516 | QCOMPARE(QStringView(empty).size(), qsizetype(0)); |
517 | QCOMPARE(static_cast<const void*>(QStringView(empty).data()), |
518 | static_cast<const void*>(empty)); |
519 | |
520 | QVERIFY( QStringView(null).isNull()); |
521 | QVERIFY( QStringView(null).isEmpty()); |
522 | QVERIFY( QStringView(empty).isEmpty()); |
523 | QVERIFY(!QStringView(empty).isNull()); |
524 | |
525 | conversion_tests(arg); |
526 | } |
527 | |
528 | template <typename Char> |
529 | void tst_QStringView::fromRange(const Char *first, const Char *last) const |
530 | { |
531 | const Char *null = nullptr; |
532 | QCOMPARE(QStringView(null, null).size(), 0); |
533 | QCOMPARE(QStringView(null, null).data(), nullptr); |
534 | QCOMPARE(QStringView(first, first).size(), 0); |
535 | QCOMPARE(static_cast<const void*>(QStringView(first, first).data()), |
536 | static_cast<const void*>(first)); |
537 | |
538 | const auto sv = QStringView(first, last); |
539 | QCOMPARE(sv.size(), last - first); |
540 | QCOMPARE(static_cast<const void*>(sv.data()), |
541 | static_cast<const void*>(first)); |
542 | |
543 | // can't call conversion_tests() here, as it requires a single object |
544 | } |
545 | |
546 | template <typename Char, typename Container> |
547 | void tst_QStringView::fromContainer() const |
548 | { |
549 | const QString s = "Hello World!" ; |
550 | |
551 | Container c; |
552 | // unspecified whether empty containers make null QStringViews |
553 | QVERIFY(QStringView(c).isEmpty()); |
554 | |
555 | QCOMPARE(sizeof(Char), sizeof(QChar)); |
556 | |
557 | const auto *data = reinterpret_cast<const Char *>(s.utf16()); |
558 | std::copy(data, data + s.size(), std::back_inserter(c)); |
559 | conversion_tests(std::move(c)); |
560 | } |
561 | |
562 | namespace help { |
563 | template <typename T> |
564 | size_t size(const T &t) { return size_t(t.size()); } |
565 | template <typename T> |
566 | size_t size(const T *t) |
567 | { |
568 | size_t result = 0; |
569 | if (t) { |
570 | while (*t++) |
571 | ++result; |
572 | } |
573 | return result; |
574 | } |
575 | size_t size(const QChar *t) |
576 | { |
577 | size_t result = 0; |
578 | if (t) { |
579 | while (!t++->isNull()) |
580 | ++result; |
581 | } |
582 | return result; |
583 | } |
584 | |
585 | template <typename T> |
586 | typename T::const_iterator cbegin(const T &t) { return t.cbegin(); } |
587 | template <typename T> |
588 | const T * cbegin(const T *t) { return t; } |
589 | |
590 | template <typename T> |
591 | typename T::const_iterator cend(const T &t) { return t.cend(); } |
592 | template <typename T> |
593 | const T * cend(const T *t) { return t + size(t); } |
594 | |
595 | template <typename T> |
596 | typename T::const_reverse_iterator crbegin(const T &t) { return t.crbegin(); } |
597 | template <typename T> |
598 | std::reverse_iterator<const T*> crbegin(const T *t) { return std::reverse_iterator<const T*>(cend(t)); } |
599 | |
600 | template <typename T> |
601 | typename T::const_reverse_iterator crend(const T &t) { return t.crend(); } |
602 | template <typename T> |
603 | std::reverse_iterator<const T*> crend(const T *t) { return std::reverse_iterator<const T*>(cbegin(t)); } |
604 | |
605 | } // namespace help |
606 | |
607 | template <typename String> |
608 | void tst_QStringView::conversion_tests(String string) const |
609 | { |
610 | // copy-construct: |
611 | { |
612 | QStringView sv = string; |
613 | |
614 | QCOMPARE(help::size(sv), help::size(string)); |
615 | |
616 | // check iterators: |
617 | |
618 | QVERIFY(std::equal(help::cbegin(string), help::cend(string), |
619 | QT_MAKE_CHECKED_ARRAY_ITERATOR(sv.cbegin(), sv.size()))); |
620 | QVERIFY(std::equal(help::cbegin(string), help::cend(string), |
621 | QT_MAKE_CHECKED_ARRAY_ITERATOR(sv.begin(), sv.size()))); |
622 | QVERIFY(std::equal(help::crbegin(string), help::crend(string), |
623 | sv.crbegin())); |
624 | QVERIFY(std::equal(help::crbegin(string), help::crend(string), |
625 | sv.rbegin())); |
626 | |
627 | QCOMPARE(sv, string); |
628 | } |
629 | |
630 | QStringView sv; |
631 | |
632 | // copy-assign: |
633 | { |
634 | sv = string; |
635 | |
636 | QCOMPARE(help::size(sv), help::size(string)); |
637 | |
638 | // check relational operators: |
639 | |
640 | QCOMPARE(sv, string); |
641 | QCOMPARE(string, sv); |
642 | |
643 | QVERIFY(!(sv != string)); |
644 | QVERIFY(!(string != sv)); |
645 | |
646 | QVERIFY(!(sv < string)); |
647 | QVERIFY(sv <= string); |
648 | QVERIFY(!(sv > string)); |
649 | QVERIFY(sv >= string); |
650 | |
651 | QVERIFY(!(string < sv)); |
652 | QVERIFY(string <= sv); |
653 | QVERIFY(!(string > sv)); |
654 | QVERIFY(string >= sv); |
655 | } |
656 | |
657 | // copy-construct from rvalue (QStringView never assumes ownership): |
658 | { |
659 | QStringView sv2 = std::move(string); |
660 | QCOMPARE(sv2, sv); |
661 | QCOMPARE(sv2, string); |
662 | } |
663 | |
664 | // copy-assign from rvalue (QStringView never assumes ownership): |
665 | { |
666 | QStringView sv2; |
667 | sv2 = std::move(string); |
668 | QCOMPARE(sv2, sv); |
669 | QCOMPARE(sv2, string); |
670 | } |
671 | } |
672 | |
673 | void tst_QStringView::comparison() |
674 | { |
675 | const QStringView aa = u"aa" ; |
676 | const QStringView upperAa = u"AA" ; |
677 | const QStringView bb = u"bb" ; |
678 | |
679 | QVERIFY(aa == aa); |
680 | QVERIFY(aa != bb); |
681 | QVERIFY(aa < bb); |
682 | QVERIFY(bb > aa); |
683 | |
684 | QCOMPARE(aa.compare(aa), 0); |
685 | QVERIFY(aa.compare(upperAa) != 0); |
686 | QCOMPARE(aa.compare(upperAa, Qt::CaseInsensitive), 0); |
687 | QVERIFY(aa.compare(bb) < 0); |
688 | QVERIFY(bb.compare(aa) > 0); |
689 | } |
690 | |
691 | namespace QStringViewOverloadResolution { |
692 | static void test(QString) = delete; |
693 | static void test(QStringView) {} |
694 | } |
695 | |
696 | // Compile-time only test: overload resolution prefers QStringView over QString |
697 | void tst_QStringView::overloadResolution() |
698 | { |
699 | { |
700 | QChar qcharArray[42] = {}; |
701 | QStringViewOverloadResolution::test(qcharArray); |
702 | QChar *qcharPointer = qcharArray; |
703 | QStringViewOverloadResolution::test(qcharPointer); |
704 | } |
705 | |
706 | { |
707 | ushort ushortArray[42] = {}; |
708 | QStringViewOverloadResolution::test(ushortArray); |
709 | ushort *ushortPointer = ushortArray; |
710 | QStringViewOverloadResolution::test(ushortPointer); |
711 | } |
712 | |
713 | { |
714 | QStringRef stringRef; |
715 | QStringViewOverloadResolution::test(stringRef); |
716 | QStringViewOverloadResolution::test(qAsConst(t&: stringRef)); |
717 | QStringViewOverloadResolution::test(std::move(stringRef)); |
718 | } |
719 | |
720 | #if defined(Q_OS_WIN) |
721 | { |
722 | wchar_t wchartArray[42] = {}; |
723 | QStringViewOverloadResolution::test(wchartArray); |
724 | QStringViewOverloadResolution::test(L"test" ); |
725 | } |
726 | #endif |
727 | |
728 | #if defined(Q_COMPILER_UNICODE_STRINGS) |
729 | { |
730 | char16_t char16Array[] = u"test" ; |
731 | QStringViewOverloadResolution::test(char16Array); |
732 | char16_t *char16Pointer = char16Array; |
733 | QStringViewOverloadResolution::test(char16Pointer); |
734 | } |
735 | #endif |
736 | |
737 | #if defined(Q_STDLIB_UNICODE_STRINGS) |
738 | { |
739 | std::u16string string; |
740 | QStringViewOverloadResolution::test(string); |
741 | QStringViewOverloadResolution::test(qAsConst(t&: string)); |
742 | QStringViewOverloadResolution::test(std::move(string)); |
743 | } |
744 | #endif |
745 | } |
746 | |
747 | QTEST_APPLESS_MAIN(tst_QStringView) |
748 | #include "tst_qstringview.moc" |
749 | |