krandomtest.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. /*
  2. This file is part of the KDE libraries
  3. SPDX-FileCopyrightText: 2016 Michael Pyne <mpyne@kde.org>
  4. SPDX-FileCopyrightText: 2016 Arne Spiegelhauer <gm2.asp@gmail.com>
  5. SPDX-License-Identifier: LGPL-2.0-only
  6. */
  7. #include <krandom.h>
  8. #include <krandomsequence.h>
  9. #include <stdlib.h>
  10. #include <QTest>
  11. #include <QThread>
  12. #include <QObject>
  13. #include <QProcess>
  14. #include <QRegularExpression>
  15. #include <QString>
  16. #include <QTextStream>
  17. #include <QVarLengthArray>
  18. #include <algorithm>
  19. #include <iostream>
  20. typedef QVarLengthArray<int> intSequenceType;
  21. static const char *binpath;
  22. #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 75)
  23. static bool seqsAreEqual(const intSequenceType &l, const intSequenceType &r)
  24. {
  25. if (l.size() != r.size()) {
  26. return false;
  27. }
  28. const intSequenceType::const_iterator last(l.end());
  29. intSequenceType::const_iterator l_first(l.begin());
  30. intSequenceType::const_iterator r_first(r.begin());
  31. while (l_first != last && *l_first == *r_first) {
  32. l_first++;
  33. r_first++;
  34. }
  35. return l_first == last;
  36. }
  37. #endif
  38. #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 72)
  39. // Fills seq with random bytes produced by a new process. Seq should already
  40. // be sized to the needed amount of random numbers.
  41. static bool getChildRandSeq(intSequenceType &seq)
  42. {
  43. QProcess subtestProcess;
  44. // Launch a separate process to generate random numbers to test first-time
  45. // seeding.
  46. subtestProcess.start(QLatin1String(binpath), QStringList() << QString::number(seq.count()));
  47. subtestProcess.waitForFinished();
  48. QTextStream childStream(subtestProcess.readAllStandardOutput());
  49. std::generate(seq.begin(), seq.end(), [&]() {
  50. int temp;
  51. childStream >> temp;
  52. return temp;
  53. });
  54. char c;
  55. childStream >> c;
  56. return c == '@' && childStream.status() == QTextStream::Ok;
  57. }
  58. #endif
  59. class KRandomTest : public QObject
  60. {
  61. Q_OBJECT
  62. private Q_SLOTS:
  63. #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 72)
  64. void test_random();
  65. #endif
  66. void test_randomString();
  67. void test_randomStringThreaded();
  68. #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 75)
  69. void test_KRS();
  70. #endif
  71. void test_shuffle();
  72. };
  73. #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 72)
  74. void KRandomTest::test_random()
  75. {
  76. int testValue = KRandom::random();
  77. QVERIFY(testValue >= 0);
  78. QVERIFY(testValue < RAND_MAX);
  79. // Verify seeding results in different numbers across different procs
  80. // See bug 362161
  81. intSequenceType out1(10);
  82. intSequenceType out2(10);
  83. QVERIFY(getChildRandSeq(out1));
  84. QVERIFY(getChildRandSeq(out2));
  85. QVERIFY(!seqsAreEqual(out1, out2));
  86. }
  87. #endif
  88. void KRandomTest::test_randomString()
  89. {
  90. const int desiredLength = 12;
  91. const QString testString = KRandom::randomString(desiredLength);
  92. const QRegularExpression outputFormat(QRegularExpression::anchoredPattern(QStringLiteral("[A-Za-z0-9]+")));
  93. const QRegularExpressionMatch match = outputFormat.match(testString);
  94. QCOMPARE(testString.length(), desiredLength);
  95. QVERIFY(match.hasMatch());
  96. }
  97. #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 75)
  98. void KRandomTest::test_KRS()
  99. {
  100. using std::all_of;
  101. using std::generate;
  102. const int maxInt = 50000;
  103. KRandomSequence krs1;
  104. KRandomSequence krs2;
  105. intSequenceType out1(10);
  106. intSequenceType out2(10);
  107. generate(out1.begin(), out1.end(), [&]() {
  108. return krs1.getInt(maxInt);
  109. });
  110. generate(out2.begin(), out2.end(), [&]() {
  111. return krs2.getInt(maxInt);
  112. });
  113. QVERIFY(!seqsAreEqual(out1, out2));
  114. QVERIFY(all_of(out1.begin(), out1.end(), [&](int x) {
  115. return x < maxInt;
  116. }));
  117. QVERIFY(all_of(out2.begin(), out2.end(), [&](int x) {
  118. return x < maxInt;
  119. }));
  120. // Compare same-seed
  121. krs1.setSeed(5000);
  122. krs2.setSeed(5000);
  123. generate(out1.begin(), out1.end(), [&]() {
  124. return krs1.getInt(maxInt);
  125. });
  126. generate(out2.begin(), out2.end(), [&]() {
  127. return krs2.getInt(maxInt);
  128. });
  129. QVERIFY(seqsAreEqual(out1, out2));
  130. QVERIFY(all_of(out1.begin(), out1.end(), [&](int x) {
  131. return x < maxInt;
  132. }));
  133. QVERIFY(all_of(out2.begin(), out2.end(), [&](int x) {
  134. return x < maxInt;
  135. }));
  136. // Compare same-seed and assignment ctor
  137. krs1 = KRandomSequence(8000);
  138. krs2 = KRandomSequence(8000);
  139. generate(out1.begin(), out1.end(), [&]() {
  140. return krs1.getInt(maxInt);
  141. });
  142. generate(out2.begin(), out2.end(), [&]() {
  143. return krs2.getInt(maxInt);
  144. });
  145. QVERIFY(seqsAreEqual(out1, out2));
  146. QVERIFY(all_of(out1.begin(), out1.end(), [&](int x) {
  147. return x < maxInt;
  148. }));
  149. QVERIFY(all_of(out2.begin(), out2.end(), [&](int x) {
  150. return x < maxInt;
  151. }));
  152. }
  153. #endif
  154. void KRandomTest::test_shuffle()
  155. {
  156. {
  157. QRandomGenerator rg(1);
  158. QList<int> list = {1, 2, 3, 4, 5};
  159. const QList<int> shuffled = {5, 2, 4, 3, 1};
  160. KRandom::shuffle(list, &rg);
  161. QCOMPARE(list, shuffled);
  162. }
  163. {
  164. QRandomGenerator rg(1);
  165. QVector<int> vector = {1, 2, 3, 4, 5};
  166. const QVector<int> shuffled = {5, 2, 4, 3, 1};
  167. KRandom::shuffle(vector, &rg);
  168. QCOMPARE(vector, shuffled);
  169. }
  170. {
  171. QRandomGenerator rg(1);
  172. std::vector<int> std_vector = {1, 2, 3, 4, 5};
  173. const std::vector<int> shuffled = {5, 2, 4, 3, 1};
  174. KRandom::shuffle(std_vector, &rg);
  175. QCOMPARE(std_vector, shuffled);
  176. }
  177. }
  178. class KRandomTestThread : public QThread
  179. {
  180. protected:
  181. void run() override
  182. {
  183. result = KRandom::randomString(32);
  184. };
  185. public:
  186. QString result;
  187. };
  188. void KRandomTest::test_randomStringThreaded()
  189. {
  190. static const int size = 5;
  191. KRandomTestThread *threads[size];
  192. for (int i = 0; i < size; ++i) {
  193. threads[i] = new KRandomTestThread();
  194. threads[i]->start();
  195. }
  196. QSet<QString> results;
  197. for (int i = 0; i < size; ++i) {
  198. threads[i]->wait(2000);
  199. results.insert(threads[i]->result);
  200. }
  201. // each thread should have returned a unique result
  202. QCOMPARE(results.size(), size);
  203. for (int i = 0; i < size; ++i) {
  204. delete threads[i];
  205. }
  206. }
  207. #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 72)
  208. // Used by getChildRandSeq... outputs random numbers to stdout and then
  209. // exits the process.
  210. static void childGenRandom(int count)
  211. {
  212. // No logic to 300, just wanted to avoid it accidentally being 2.4B...
  213. if (count <= 0 || count > 300) {
  214. exit(-1);
  215. }
  216. while (--count > 0) {
  217. std::cout << KRandom::random() << ' ';
  218. }
  219. std::cout << KRandom::random() << '@';
  220. exit(0);
  221. }
  222. #endif
  223. // Manually implemented to dispatch to child process if needed to support
  224. // subtests
  225. int main([[maybe_unused]] int argc, char *argv[])
  226. {
  227. #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 72)
  228. if (argc > 1) {
  229. childGenRandom(std::atoi(argv[1]));
  230. Q_UNREACHABLE();
  231. }
  232. #endif
  233. binpath = argv[0];
  234. KRandomTest randomTest;
  235. return QTest::qExec(&randomTest);
  236. }
  237. #include "krandomtest.moc"