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 <QTextCodec>
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);
83  const QDateTime dt1 = parseDateTimeStringOptimized(c);
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");
88  dt3.setOffsetFromUtc(0);
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  QTextCodec *latin1 = QTextCodec::codecForName("latin1");
134  QTextCodec *cp1251 = QTextCodec::codecForName("windows-1251");
135  QTextCodec *utf8 = QTextCodec::codecForName("UTF-8");
136  const QString testEnglish = QStringLiteral(u"test");
137  const QString testRussian = QStringLiteral(u"тест");
138  bool okEn1 = latin1->toUnicode(latin1->fromUnicode(testEnglish)) == testEnglish;
139  bool okEn2 = utf8->toUnicode(utf8->fromUnicode(testEnglish)) == testEnglish;
140  bool okRu1 = cp1251->toUnicode(cp1251->fromUnicode(testRussian)) == testRussian;
141  bool okRu2 = utf8->toUnicode(utf8->fromUnicode(testRussian)) == testRussian;
142  QVERIFY2(okEn1, "English \"test\" equal after round-trip with latin1");
143  QVERIFY2(okEn2, "English \"test\" equal after round-trip with utf8");
144  QVERIFY2(okRu1, "Russian \"test\" equal after round-trip with cp1251");
145  QVERIFY2(okRu2, "Russian \"test\" equal after round-trip with utf8");
146  }
147 
148  void CTestStringUtils::testSimplify()
149  {
150  const auto inputChars = u8"ŠŽšžŸÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïñòóôõöùúûüýÿ";
151  const auto outputChars = "SZszYAAAAAACEEEEIIIINOOOOOUUUUYaaaaaaceeeeiiiinooooouuuuyy";
152  const QString input = QString::fromUtf8(reinterpret_cast<const char *>(inputChars));
153  const QString output = QLatin1String(outputChars);
154  QCOMPARE(simplifyAccents(input), output);
155  QCOMPARE(simplifyByDecomposition(input), output);
156  }
157 } // namespace MiscTest
158 
161 
162 #include "teststringutils.moc"
163 
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:35
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:78
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:65
SWIFT_MISC_EXPORT QDateTime parseDateTimeStringOptimized(const QString &dateTimeString)
Parse yyyyMMddHHmmsszzz strings optimized.
SWIFTTEST_APPLESS_MAIN(MiscTest::CTestStringUtils)
main