File indexing completed on 2024-09-08 12:14:23
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2016 Michael Pyne <mpyne@kde.org> 0004 SPDX-FileCopyrightText: 2016 Arne Spiegelhauer <gm2.asp@gmail.com> 0005 0006 SPDX-License-Identifier: LGPL-2.0-only 0007 */ 0008 0009 #include <krandom.h> 0010 #include <krandomsequence.h> 0011 #include <stdlib.h> 0012 0013 #include <QTest> 0014 #include <QThread> 0015 0016 #include <QObject> 0017 #include <QProcess> 0018 #include <QRegularExpression> 0019 #include <QString> 0020 #include <QTextStream> 0021 #include <QVarLengthArray> 0022 0023 #include <algorithm> 0024 #include <iostream> 0025 0026 typedef QVarLengthArray<int> intSequenceType; 0027 0028 static const char *binpath; 0029 0030 #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 75) 0031 static bool seqsAreEqual(const intSequenceType &l, const intSequenceType &r) 0032 { 0033 if (l.size() != r.size()) { 0034 return false; 0035 } 0036 const intSequenceType::const_iterator last(l.end()); 0037 0038 intSequenceType::const_iterator l_first(l.begin()); 0039 intSequenceType::const_iterator r_first(r.begin()); 0040 0041 while (l_first != last && *l_first == *r_first) { 0042 l_first++; 0043 r_first++; 0044 } 0045 0046 return l_first == last; 0047 } 0048 #endif 0049 0050 #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 72) 0051 // Fills seq with random bytes produced by a new process. Seq should already 0052 // be sized to the needed amount of random numbers. 0053 static bool getChildRandSeq(intSequenceType &seq) 0054 { 0055 QProcess subtestProcess; 0056 0057 // Launch a separate process to generate random numbers to test first-time 0058 // seeding. 0059 subtestProcess.start(QLatin1String(binpath), QStringList() << QString::number(seq.count())); 0060 subtestProcess.waitForFinished(); 0061 0062 QTextStream childStream(subtestProcess.readAllStandardOutput()); 0063 0064 std::generate(seq.begin(), seq.end(), [&]() { 0065 int temp; 0066 childStream >> temp; 0067 return temp; 0068 }); 0069 0070 char c; 0071 childStream >> c; 0072 return c == '@' && childStream.status() == QTextStream::Ok; 0073 } 0074 #endif 0075 0076 class KRandomTest : public QObject 0077 { 0078 Q_OBJECT 0079 0080 private Q_SLOTS: 0081 #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 72) 0082 void test_random(); 0083 #endif 0084 void test_randomString(); 0085 void test_randomStringThreaded(); 0086 #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 75) 0087 void test_KRS(); 0088 #endif 0089 void test_shuffle(); 0090 }; 0091 0092 #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 72) 0093 void KRandomTest::test_random() 0094 { 0095 int testValue = KRandom::random(); 0096 0097 QVERIFY(testValue >= 0); 0098 QVERIFY(testValue < RAND_MAX); 0099 0100 // Verify seeding results in different numbers across different procs 0101 // See bug 362161 0102 intSequenceType out1(10); 0103 intSequenceType out2(10); 0104 0105 QVERIFY(getChildRandSeq(out1)); 0106 QVERIFY(getChildRandSeq(out2)); 0107 0108 QVERIFY(!seqsAreEqual(out1, out2)); 0109 } 0110 #endif 0111 0112 void KRandomTest::test_randomString() 0113 { 0114 const int desiredLength = 12; 0115 const QString testString = KRandom::randomString(desiredLength); 0116 const QRegularExpression outputFormat(QRegularExpression::anchoredPattern(QStringLiteral("[A-Za-z0-9]+"))); 0117 const QRegularExpressionMatch match = outputFormat.match(testString); 0118 0119 QCOMPARE(testString.length(), desiredLength); 0120 QVERIFY(match.hasMatch()); 0121 } 0122 0123 #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 75) 0124 void KRandomTest::test_KRS() 0125 { 0126 using std::all_of; 0127 using std::generate; 0128 0129 const int maxInt = 50000; 0130 KRandomSequence krs1; 0131 KRandomSequence krs2; 0132 intSequenceType out1(10); 0133 intSequenceType out2(10); 0134 0135 generate(out1.begin(), out1.end(), [&]() { 0136 return krs1.getInt(maxInt); 0137 }); 0138 generate(out2.begin(), out2.end(), [&]() { 0139 return krs2.getInt(maxInt); 0140 }); 0141 QVERIFY(!seqsAreEqual(out1, out2)); 0142 QVERIFY(all_of(out1.begin(), out1.end(), [&](int x) { 0143 return x < maxInt; 0144 })); 0145 QVERIFY(all_of(out2.begin(), out2.end(), [&](int x) { 0146 return x < maxInt; 0147 })); 0148 0149 // Compare same-seed 0150 krs1.setSeed(5000); 0151 krs2.setSeed(5000); 0152 0153 generate(out1.begin(), out1.end(), [&]() { 0154 return krs1.getInt(maxInt); 0155 }); 0156 generate(out2.begin(), out2.end(), [&]() { 0157 return krs2.getInt(maxInt); 0158 }); 0159 QVERIFY(seqsAreEqual(out1, out2)); 0160 QVERIFY(all_of(out1.begin(), out1.end(), [&](int x) { 0161 return x < maxInt; 0162 })); 0163 QVERIFY(all_of(out2.begin(), out2.end(), [&](int x) { 0164 return x < maxInt; 0165 })); 0166 0167 // Compare same-seed and assignment ctor 0168 0169 krs1 = KRandomSequence(8000); 0170 krs2 = KRandomSequence(8000); 0171 0172 generate(out1.begin(), out1.end(), [&]() { 0173 return krs1.getInt(maxInt); 0174 }); 0175 generate(out2.begin(), out2.end(), [&]() { 0176 return krs2.getInt(maxInt); 0177 }); 0178 QVERIFY(seqsAreEqual(out1, out2)); 0179 QVERIFY(all_of(out1.begin(), out1.end(), [&](int x) { 0180 return x < maxInt; 0181 })); 0182 QVERIFY(all_of(out2.begin(), out2.end(), [&](int x) { 0183 return x < maxInt; 0184 })); 0185 } 0186 #endif 0187 0188 void KRandomTest::test_shuffle() 0189 { 0190 { 0191 QRandomGenerator rg(1); 0192 QList<int> list = {1, 2, 3, 4, 5}; 0193 const QList<int> shuffled = {5, 2, 4, 3, 1}; 0194 KRandom::shuffle(list, &rg); 0195 QCOMPARE(list, shuffled); 0196 } 0197 0198 { 0199 QRandomGenerator rg(1); 0200 QVector<int> vector = {1, 2, 3, 4, 5}; 0201 const QVector<int> shuffled = {5, 2, 4, 3, 1}; 0202 KRandom::shuffle(vector, &rg); 0203 QCOMPARE(vector, shuffled); 0204 } 0205 0206 { 0207 QRandomGenerator rg(1); 0208 std::vector<int> std_vector = {1, 2, 3, 4, 5}; 0209 const std::vector<int> shuffled = {5, 2, 4, 3, 1}; 0210 KRandom::shuffle(std_vector, &rg); 0211 QCOMPARE(std_vector, shuffled); 0212 } 0213 } 0214 0215 class KRandomTestThread : public QThread 0216 { 0217 protected: 0218 void run() override 0219 { 0220 result = KRandom::randomString(32); 0221 }; 0222 0223 public: 0224 QString result; 0225 }; 0226 0227 void KRandomTest::test_randomStringThreaded() 0228 { 0229 static const int size = 5; 0230 KRandomTestThread *threads[size]; 0231 for (int i = 0; i < size; ++i) { 0232 threads[i] = new KRandomTestThread(); 0233 threads[i]->start(); 0234 } 0235 QSet<QString> results; 0236 for (int i = 0; i < size; ++i) { 0237 threads[i]->wait(2000); 0238 results.insert(threads[i]->result); 0239 } 0240 // each thread should have returned a unique result 0241 QCOMPARE(results.size(), size); 0242 for (int i = 0; i < size; ++i) { 0243 delete threads[i]; 0244 } 0245 } 0246 0247 #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 72) 0248 // Used by getChildRandSeq... outputs random numbers to stdout and then 0249 // exits the process. 0250 static void childGenRandom(int count) 0251 { 0252 // No logic to 300, just wanted to avoid it accidentally being 2.4B... 0253 if (count <= 0 || count > 300) { 0254 exit(-1); 0255 } 0256 0257 while (--count > 0) { 0258 std::cout << KRandom::random() << ' '; 0259 } 0260 0261 std::cout << KRandom::random() << '@'; 0262 exit(0); 0263 } 0264 #endif 0265 0266 // Manually implemented to dispatch to child process if needed to support 0267 // subtests 0268 int main([[maybe_unused]] int argc, char *argv[]) 0269 { 0270 #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 72) 0271 if (argc > 1) { 0272 childGenRandom(std::atoi(argv[1])); 0273 Q_UNREACHABLE(); 0274 } 0275 #endif 0276 0277 binpath = argv[0]; 0278 KRandomTest randomTest; 0279 return QTest::qExec(&randomTest); 0280 } 0281 0282 #include "krandomtest.moc"