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