swift
teststringutils.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 <QElapsedTimer>
12 #include <QTest>
13 #include <QTime>
14 
15 #include "test.h"
16 
17 #include "misc/stringutils.h"
18 
19 using namespace swift::misc;
20 
21 namespace MiscTest
22 {
24  class CTestStringUtils : public QObject
25  {
26  Q_OBJECT
27 
28  private slots:
29  void testRemove();
30  void testContains();
31  void testIndexOf();
32  void testSplit();
33  void testTimestampParsing();
34  void testCodecs();
35  void testSimplify();
36  };
37 
38  void CTestStringUtils::testRemove()
39  {
40  QString s = "loUwP PeERr69";
41  QVERIFY2(removeChars(s, [](QChar c) { return !c.isUpper(); }) == "UPPER",
42  "Test removing characters by predicate");
43  }
44 
45  void CTestStringUtils::testContains()
46  {
47  QString s = "string with a numb3r";
48  QVERIFY2(containsChar(s, [](QChar c) { return c.isNumber(); }), "Test contains character by predicate");
49  s = "string without a number";
50  QVERIFY2(!containsChar(s, [](QChar c) { return c.isNumber(); }), "Test not contains character by predicate");
51  }
52 
53  void CTestStringUtils::testIndexOf()
54  {
55  QString s = "string with a numb3r";
56  QVERIFY2(indexOfChar(s, [](QChar c) { return c.isNumber(); }) == 18, "Test index of character by predicate");
57  s = "string without a number";
58  QVERIFY2(indexOfChar(s, [](QChar c) { return c.isNumber(); }) == -1,
59  "Test not index of character by predicate");
60  }
61 
62  void CTestStringUtils::testSplit()
63  {
64  const QString s = "line one\nline two\r\nline three\n";
65  QStringList lines = splitLines(s);
66  QVERIFY2(lines.size() == 3, "Test split string into lines: correct number of lines");
67  QVERIFY2(lines[0] == "line one", "Test split string into lines: correct first line");
68  QVERIFY2(lines[1] == "line two", "Test split string into lines: correct second line");
69  QVERIFY2(lines[2] == "line three", "Test split string into lines: correct third line");
70  }
71 
72  void CTestStringUtils::testTimestampParsing()
73  {
74  const QStringList dts({ "2018-01-01 11:11:11", "2012-05-09 03:04:05.777", "2012-05-09 00:00:00.000",
75  "2015-12-31 03:04:05", "1999-12-31 23:59:59.999", "1975-01-01 14:13:17",
76  "1982-05-09 03:01:05.123", "2000-05-02 00:04:00.000", "2002-12-31 03:34:33",
77  "1992-11-01 21:59:29.999" });
78 
79  const int size = QString("yyyyMMddHHmmss").size();
80  for (const QString &dt : dts)
81  {
82  const QString c = removeDateTimeSeparators(dt);
84  const QDateTime dt2 =
85  (c.length() == size) ? fromStringUtc(c, "yyyyMMddHHmmss") : fromStringUtc(c, "yyyyMMddHHmmsszzz");
86  QDateTime dt3 = (c.length() == size) ? QDateTime::fromString(c, "yyyyMMddHHmmss") :
87  QDateTime::fromString(c, "yyyyMMddHHmmsszzz");
89 
90  const qint64 ms1 = dt1.toMSecsSinceEpoch();
91  const qint64 ms2 = dt2.toMSecsSinceEpoch();
92  const qint64 delta = ms1 - ms2;
93 
94  QVERIFY2(dt1 == dt2, "Expect same results of QDateTime");
95  QVERIFY2(dt1 == dt3, "Expect same results of QDateTime");
96  QVERIFY2(delta == 0, "Expect same results timestamp");
97  }
98 
99  // performance
100  int constexpr Loops = 10000;
101  QElapsedTimer time;
102  time.start();
103  for (int i = 0; i < Loops; i++)
104  {
105  for (const QString &dt : dts)
106  {
107  const QString c = removeDateTimeSeparators(dt);
108  const QDateTime dateTime = parseDateTimeStringOptimized(c);
110  Q_UNUSED(dateTime); // avoid optimizing out of call
111  }
112  }
113  const int elapsedOptimized = time.restart();
114 
115  for (int i = 0; i < Loops; i++)
116  {
117  for (const QString &dt : dts)
118  {
119  const QString c = removeDateTimeSeparators(dt);
120  const QDateTime dateTime =
121  (c.length() == size) ? fromStringUtc(c, "yyyyMMddHHmmss") : fromStringUtc(c, "yyyyMMddHHmmsszzz");
122  Q_UNUSED(dateTime); // avoid optimizing out of call
123  }
124  }
125  const int elapsedQt = time.restart();
126 
127  qDebug() << "Parsing date/time, optimized" << elapsedOptimized << "vs. QDateTime: " << elapsedQt;
128  QVERIFY2(elapsedOptimized < elapsedQt, "Expect optimized being faster as QDateTim::fromString");
129  }
130 
131  void CTestStringUtils::testCodecs()
132  {
133  const QStringConverter::Encoding latin1_enc = QStringDecoder::encodingForName("latin1").value();
134  const QStringConverter::Encoding utf8_enc = QStringDecoder::encodingForName("UTF-8").value();
135 
136  QStringDecoder latin1_decoder = QStringDecoder(latin1_enc);
137  QStringEncoder latin1_encoder = QStringEncoder(latin1_enc);
138  QStringDecoder utf8_decoder = QStringDecoder(utf8_enc);
139  QStringEncoder utf8_encoder = QStringEncoder(utf8_enc);
140 
141  const QString testEnglish = QStringLiteral(u"test");
142  const QString testRussian = QStringLiteral(u"тест");
143  bool okEn1 = latin1_decoder(latin1_encoder(testEnglish)) == testEnglish;
144  bool okEn2 = utf8_decoder(utf8_encoder(testEnglish)) == testEnglish;
145  bool okRu2 = utf8_decoder(utf8_encoder(testRussian)) == testRussian;
146  QVERIFY2(okEn1, "English \"test\" equal after round-trip with latin1");
147  QVERIFY2(okEn2, "English \"test\" equal after round-trip with utf8");
148  QVERIFY2(okRu2, "Russian \"test\" equal after round-trip with utf8");
149  }
150 
151  void CTestStringUtils::testSimplify()
152  {
153  const auto inputChars = u8"ŠŽšžŸÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïñòóôõöùúûüýÿ";
154  const auto outputChars = "SZszYAAAAAACEEEEIIIINOOOOOUUUUYaaaaaaceeeeiiiinooooouuuuyy";
155  const QString input = QString::fromUtf8(reinterpret_cast<const char *>(inputChars));
156  const QString output = QLatin1String(outputChars);
157  QCOMPARE(simplifyAccents(input), output);
158  QCOMPARE(simplifyByDecomposition(input), output);
159  }
160 } // namespace MiscTest
161 
164 
165 #include "teststringutils.moc"
166 
Testing string utilities.
Free functions in swift::misc.
QString removeChars(const QString &s, F predicate)
Return a string with characters removed that match the given predicate.
Definition: stringutils.h:34
SWIFT_MISC_EXPORT QString removeDateTimeSeparators(const QString &s)
Remove the typical separators such as "-", " ".
SWIFT_MISC_EXPORT QString simplifyByDecomposition(const QString &candidate)
Remove accents / diacritic marks from a string by doing a Unicode decomposition and removing mark cha...
SWIFT_MISC_EXPORT QString simplifyAccents(const QString &candidate)
Remove accents / diacritic marks from a string.
int indexOfChar(const QString &s, F predicate)
Index of first character in the string matching the given predicate, or -1 if not found.
Definition: stringutils.h:77
SWIFT_MISC_EXPORT QStringList splitLines(const QString &s)
Split a string into multiple lines. Blank lines are skipped.
SWIFT_MISC_EXPORT QDateTime fromStringUtc(const QString &dateTimeString, const QString &format)
Same as QDateTime::fromString but QDateTime will be set to UTC.
bool containsChar(const QString &s, F predicate)
True if any character in the string matches the given predicate.
Definition: stringutils.h:64
SWIFT_MISC_EXPORT QDateTime parseDateTimeStringOptimized(const QString &dateTimeString)
Parse yyyyMMddHHmmsszzz strings optimized.
bool isNumber(char32_t ucs4)
bool isUpper(char32_t ucs4)
QDateTime fromString(QStringView string, QStringView format, QCalendar cal)
void setTimeZone(const QTimeZone &toZone, QDateTime::TransitionResolution resolve)
qint64 toMSecsSinceEpoch() const const
qint64 restart()
qsizetype size() const const
QString fromUtf8(QByteArrayView str)
qsizetype length() const const
qsizetype size() const const
std::optional< QStringConverter::Encoding > encodingForName(QAnyStringView name)
QCOMPARE(actual, expected)
QVERIFY2(condition, message)
QTimeZone utc()
SWIFTTEST_APPLESS_MAIN(MiscTest::CTestStringUtils)
main