1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QDebug>
30#include <QFile>
31#if QT_CONFIG(process)
32# include <QProcess>
33#endif
34#include <QSharedMemory>
35#include <QTest>
36#include <QThread>
37#include <QElapsedTimer>
38#include <QScopeGuard>
39
40#define EXISTING_SHARE "existing"
41#define EXISTING_SIZE 1024
42
43Q_DECLARE_METATYPE(QSharedMemory::SharedMemoryError)
44Q_DECLARE_METATYPE(QSharedMemory::AccessMode)
45
46class tst_QSharedMemory : public QObject
47{
48 Q_OBJECT
49
50public:
51 tst_QSharedMemory();
52 virtual ~tst_QSharedMemory();
53
54public Q_SLOTS:
55 void init();
56 void cleanup();
57
58
59private slots:
60 // basics
61 void constructor();
62 void key_data();
63 void key();
64 void create_data();
65 void create();
66 void attach_data();
67 void attach();
68 void lock();
69
70 // custom edge cases
71#ifndef Q_OS_HPUX
72 void removeWhileAttached();
73#endif
74 void emptyMemory();
75#if !defined(Q_OS_WIN)
76 void readOnly();
77#endif
78
79 // basics all together
80#ifndef Q_OS_HPUX
81 void simpleProducerConsumer_data();
82 void simpleProducerConsumer();
83 void simpleDoubleProducerConsumer();
84#endif
85
86 // with threads
87 void simpleThreadedProducerConsumer_data();
88 void simpleThreadedProducerConsumer();
89
90 // with processes
91 void simpleProcessProducerConsumer_data();
92 void simpleProcessProducerConsumer();
93
94 // extreme cases
95 void useTooMuchMemory();
96#if !defined(Q_OS_HPUX)
97 void attachTooMuch();
98#endif
99
100 // unique keys
101 void uniqueKey_data();
102 void uniqueKey();
103
104protected:
105 int remove(const QString &key);
106
107 QString rememberKey(const QString &key)
108 {
109 if (key == EXISTING_SHARE)
110 return key;
111 if (!keys.contains(str: key)) {
112 keys.append(t: key);
113 remove(key);
114 }
115 return key;
116 }
117
118 QStringList keys;
119 QList<QSharedMemory*> jail;
120 QSharedMemory *existingSharedMemory;
121
122private:
123 const QString m_helperBinary;
124};
125
126tst_QSharedMemory::tst_QSharedMemory()
127 : existingSharedMemory(0)
128 , m_helperBinary("producerconsumer_helper")
129{
130}
131
132tst_QSharedMemory::~tst_QSharedMemory()
133{
134}
135
136void tst_QSharedMemory::init()
137{
138 existingSharedMemory = new QSharedMemory(EXISTING_SHARE);
139 if (!existingSharedMemory->create(EXISTING_SIZE)) {
140 QCOMPARE(existingSharedMemory->error(), QSharedMemory::AlreadyExists);
141 }
142}
143
144void tst_QSharedMemory::cleanup()
145{
146 delete existingSharedMemory;
147 qDeleteAll(begin: jail.begin(), end: jail.end());
148 jail.clear();
149
150 keys.append(EXISTING_SHARE);
151 for (int i = 0; i < keys.count(); ++i) {
152 QSharedMemory sm(keys.at(i));
153 if (!sm.create(size: 1024)) {
154 //if (sm.error() != QSharedMemory::KeyError)
155 // qWarning() << "test cleanup: remove failed:" << keys.at(i) << sm.error() << sm.errorString();
156 sm.attach();
157 sm.detach();
158 remove(key: keys.at(i));
159 }
160 }
161}
162
163#ifndef Q_OS_WIN
164#include <private/qsharedmemory_p.h>
165#include <sys/types.h>
166#ifndef QT_POSIX_IPC
167#include <sys/ipc.h>
168#include <sys/shm.h>
169#else
170#include <sys/mman.h>
171#endif // QT_POSIX_IPC
172#include <errno.h>
173#endif
174
175int tst_QSharedMemory::remove(const QString &key)
176{
177#ifdef Q_OS_WIN
178 Q_UNUSED(key);
179 return 0;
180#else
181 // On unix the shared memory might exists from a previously failed test
182 // or segfault, remove it it does
183 if (key.isEmpty())
184 return -1;
185
186 // ftok requires that an actual file exists somewhere
187 QString fileName = QSharedMemoryPrivate::makePlatformSafeKey(key);
188 if (!QFile::exists(fileName)) {
189 //qDebug() << "exits failed";
190 return -2;
191 }
192
193#ifndef QT_POSIX_IPC
194 int unix_key = ftok(pathname: fileName.toLatin1().constData(), proj_id: 'Q');
195 if (-1 == unix_key) {
196 qDebug() << "ftok failed";
197 return -3;
198 }
199
200 int id = shmget(key: unix_key, size: 0, shmflg: 0600);
201 if (-1 == id) {
202 qDebug() << "shmget failed" << strerror(errno);
203 return -4;
204 }
205
206 struct shmid_ds shmid_ds;
207 if (-1 == shmctl(shmid: id, IPC_RMID, buf: &shmid_ds)) {
208 qDebug() << "shmctl failed";
209 return -5;
210 }
211#else
212 if (shm_unlink(QFile::encodeName(fileName).constData()) == -1) {
213 qDebug() << "shm_unlink failed";
214 return -5;
215 }
216#endif // QT_POSIX_IPC
217
218 return QFile::remove(fileName);
219#endif // Q_OS_WIN
220}
221
222/*!
223 Tests the default values
224 */
225void tst_QSharedMemory::constructor()
226{
227 QSharedMemory sm;
228 QCOMPARE(sm.key(), QString());
229 QVERIFY(!sm.isAttached());
230 QVERIFY(!sm.data());
231 QCOMPARE(sm.size(), 0);
232 QCOMPARE(sm.error(), QSharedMemory::NoError);
233 QCOMPARE(sm.errorString(), QString());
234}
235
236void tst_QSharedMemory::key_data()
237{
238 QTest::addColumn<QString>(name: "constructorKey");
239 QTest::addColumn<QString>(name: "setKey");
240 QTest::addColumn<QString>(name: "setNativeKey");
241
242 QTest::newRow(dataTag: "null, null, null") << QString() << QString() << QString();
243 QTest::newRow(dataTag: "one, null, null") << QString("one") << QString() << QString();
244 QTest::newRow(dataTag: "null, one, null") << QString() << QString("one") << QString();
245 QTest::newRow(dataTag: "null, null, one") << QString() << QString() << QString("one");
246 QTest::newRow(dataTag: "one, two, null") << QString("one") << QString("two") << QString();
247 QTest::newRow(dataTag: "one, null, two") << QString("one") << QString() << QString("two");
248 QTest::newRow(dataTag: "null, one, two") << QString() << QString("one") << QString("two");
249 QTest::newRow(dataTag: "one, two, three") << QString("one") << QString("two") << QString("three");
250 QTest::newRow(dataTag: "invalid") << QString("o/e") << QString("t/o") << QString("|x");
251}
252
253/*!
254 Basic key testing
255 */
256void tst_QSharedMemory::key()
257{
258 QFETCH(QString, constructorKey);
259 QFETCH(QString, setKey);
260 QFETCH(QString, setNativeKey);
261
262 QSharedMemory sm(constructorKey);
263 QCOMPARE(sm.key(), constructorKey);
264 QCOMPARE(sm.nativeKey().isEmpty(), constructorKey.isEmpty());
265 sm.setKey(setKey);
266 QCOMPARE(sm.key(), setKey);
267 QCOMPARE(sm.nativeKey().isEmpty(), setKey.isEmpty());
268 sm.setNativeKey(setNativeKey);
269 QVERIFY(sm.key().isNull());
270 QCOMPARE(sm.nativeKey(), setNativeKey);
271 QCOMPARE(sm.isAttached(), false);
272
273 QCOMPARE(sm.error(), QSharedMemory::NoError);
274 QCOMPARE(sm.errorString(), QString());
275 QVERIFY(!sm.data());
276 QCOMPARE(sm.size(), 0);
277
278 QCOMPARE(sm.detach(), false);
279}
280
281void tst_QSharedMemory::create_data()
282{
283 QTest::addColumn<QString>(name: "key");
284 QTest::addColumn<int>(name: "size");
285 QTest::addColumn<bool>(name: "canCreate");
286 QTest::addColumn<QSharedMemory::SharedMemoryError>(name: "error");
287
288 QTest::newRow(dataTag: "null key") << QString() << 1024
289 << false << QSharedMemory::KeyError;
290 QTest::newRow(dataTag: "-1 size") << QString("negsize") << -1
291 << false << QSharedMemory::InvalidSize;
292 QTest::newRow(dataTag: "nor size") << QString("norsize") << 1024
293 << true << QSharedMemory::NoError;
294 QTest::newRow(dataTag: "already exists") << QString(EXISTING_SHARE) << EXISTING_SIZE
295 << false << QSharedMemory::AlreadyExists;
296}
297
298/*!
299 Basic create testing
300 */
301void tst_QSharedMemory::create()
302{
303 QFETCH(QString, key);
304 QFETCH(int, size);
305 QFETCH(bool, canCreate);
306 QFETCH(QSharedMemory::SharedMemoryError, error);
307
308 QSharedMemory sm(rememberKey(key));
309 QCOMPARE(sm.create(size), canCreate);
310 if (sm.error() != error)
311 qDebug() << sm.errorString();
312 QCOMPARE(sm.key(), key);
313 if (canCreate) {
314 QCOMPARE(sm.errorString(), QString());
315 QVERIFY(sm.data() != 0);
316 QVERIFY(sm.size() != 0);
317 } else {
318 QVERIFY(!sm.data());
319 QVERIFY(sm.errorString() != QString());
320 }
321}
322
323void tst_QSharedMemory::attach_data()
324{
325 QTest::addColumn<QString>(name: "key");
326 QTest::addColumn<bool>(name: "exists");
327 QTest::addColumn<QSharedMemory::SharedMemoryError>(name: "error");
328
329 QTest::newRow(dataTag: "null key") << QString() << false << QSharedMemory::KeyError;
330 QTest::newRow(dataTag: "doesn't exists") << QString("doesntexists") << false << QSharedMemory::NotFound;
331
332 // HPUX doesn't allow for multiple attaches per process.
333#ifndef Q_OS_HPUX
334 QTest::newRow(dataTag: "already exists") << QString(EXISTING_SHARE) << true << QSharedMemory::NoError;
335#endif
336}
337
338/*!
339 Basic attach/detach testing
340 */
341void tst_QSharedMemory::attach()
342{
343 QFETCH(QString, key);
344 QFETCH(bool, exists);
345 QFETCH(QSharedMemory::SharedMemoryError, error);
346
347 QSharedMemory sm(key);
348 QCOMPARE(sm.attach(), exists);
349 QCOMPARE(sm.isAttached(), exists);
350 QCOMPARE(sm.error(), error);
351 QCOMPARE(sm.key(), key);
352 if (exists) {
353 QVERIFY(sm.data() != 0);
354 QVERIFY(sm.size() != 0);
355 QCOMPARE(sm.errorString(), QString());
356 QVERIFY(sm.detach());
357 // Make sure detach doesn't screw up something and we can't re-attach.
358 QVERIFY(sm.attach());
359 QVERIFY(sm.data() != 0);
360 QVERIFY(sm.size() != 0);
361 QVERIFY(sm.detach());
362 QCOMPARE(sm.size(), 0);
363 QVERIFY(!sm.data());
364 } else {
365 QVERIFY(!sm.data());
366 QCOMPARE(sm.size(), 0);
367 QVERIFY(sm.errorString() != QString());
368 QVERIFY(!sm.detach());
369 }
370}
371
372void tst_QSharedMemory::lock()
373{
374 QSharedMemory shm;
375 QVERIFY(!shm.lock());
376 QCOMPARE(shm.error(), QSharedMemory::LockError);
377
378 shm.setKey(QLatin1String("qsharedmemory"));
379
380 QVERIFY(!shm.lock());
381 QCOMPARE(shm.error(), QSharedMemory::LockError);
382
383 QVERIFY(shm.create(100));
384 QVERIFY(shm.lock());
385 QTest::ignoreMessage(type: QtWarningMsg, message: "QSharedMemory::lock: already locked");
386 QVERIFY(shm.lock());
387 // we didn't unlock(), so ignore the warning from auto-detach in destructor
388 QTest::ignoreMessage(type: QtWarningMsg, message: "QSharedMemory::lock: already locked");
389}
390
391/*!
392 Other shared memory are allowed to be attached after we remove,
393 but new shared memory are not allowed to attach after a remove.
394 */
395// HPUX doesn't allow for multiple attaches per process.
396#ifndef Q_OS_HPUX
397void tst_QSharedMemory::removeWhileAttached()
398{
399 rememberKey(key: "one");
400
401 // attach 1
402 QSharedMemory *smOne = new QSharedMemory(QLatin1String("one"));
403 QVERIFY(smOne->create(1024));
404 QVERIFY(smOne->isAttached());
405
406 // attach 2
407 QSharedMemory *smTwo = new QSharedMemory(QLatin1String("one"));
408 QVERIFY(smTwo->attach());
409 QVERIFY(smTwo->isAttached());
410
411 // detach 1 and remove, remove one first to catch another error.
412 delete smOne;
413 delete smTwo;
414
415 // three shouldn't be able to attach
416 QSharedMemory smThree(QLatin1String("one"));
417 QVERIFY(!smThree.attach());
418 QCOMPARE(smThree.error(), QSharedMemory::NotFound);
419}
420#endif
421
422/*!
423 The memory should be set to 0 after created.
424 */
425void tst_QSharedMemory::emptyMemory()
426{
427 QSharedMemory sm(rememberKey(key: QLatin1String("voidland")));
428 int size = 1024;
429 QVERIFY(sm.create(size, QSharedMemory::ReadOnly));
430 char *get = (char*)sm.data();
431 char null = 0;
432 for (int i = 0; i < size; ++i)
433 QCOMPARE(get[i], null);
434}
435
436/*!
437 Verify that attach with ReadOnly is actually read only
438 by writing to data and causing a segfault.
439*/
440// This test opens a crash dialog on Windows.
441#if !defined(Q_OS_WIN)
442void tst_QSharedMemory::readOnly()
443{
444#if !QT_CONFIG(process)
445 QSKIP("No qprocess support", SkipAll);
446#elif defined(Q_OS_MACOS)
447 QSKIP("QTBUG-59936: Times out on macOS", SkipAll);
448#elif defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)
449 QSKIP("ASan prevents the crash this test is looking for.", SkipAll);
450#else
451 rememberKey(key: "readonly_segfault");
452
453 // Add the executable's directory to path so that we can find the test helper next to it
454 // in a cross-platform way. We must do this because the CWD is not pointing to this directory
455 // in debug-and-release builds.
456 QByteArray path = qgetenv(varName: "PATH");
457 qputenv(varName: "PATH",
458 value: path + QDir::listSeparator().toLatin1()
459 + QCoreApplication::applicationDirPath().toLocal8Bit());
460 auto restore = qScopeGuard(f: [&] { qputenv(varName: "PATH", value: path); });
461
462 // ### on windows disable the popup somehow
463 QProcess p;
464 p.start(program: m_helperBinary, arguments: QStringList("readonly_segfault"));
465 p.setProcessChannelMode(QProcess::ForwardedChannels);
466 p.waitForFinished();
467 QCOMPARE(p.error(), QProcess::Crashed);
468#endif
469}
470#endif
471
472/*!
473 Keep making shared memory until the kernel stops us.
474 */
475void tst_QSharedMemory::useTooMuchMemory()
476{
477#ifdef Q_OS_LINUX
478 bool success = true;
479 int count = 0;
480 while (success) {
481 QString key = QLatin1String("maxmemorytest_") + QString::number(count++);
482 QSharedMemory *sm = new QSharedMemory(rememberKey(key));
483 QVERIFY(sm);
484 jail.append(t: sm);
485 int size = 32768 * 1024;
486 success = sm->create(size);
487 if (!success && sm->error() == QSharedMemory::AlreadyExists) {
488 // left over from a crash, clean it up
489 sm->attach();
490 sm->detach();
491 success = sm->create(size);
492 }
493
494 if (!success) {
495 QVERIFY(!sm->isAttached());
496 QCOMPARE(sm->key(), key);
497 QCOMPARE(sm->size(), 0);
498 QVERIFY(!sm->data());
499 if (sm->error() != QSharedMemory::OutOfResources)
500 qDebug() << sm->error() << sm->errorString();
501 // ### Linux won't return OutOfResources if there are not enough semaphores to use.
502 QVERIFY(sm->error() == QSharedMemory::OutOfResources
503 || sm->error() == QSharedMemory::LockError);
504 QVERIFY(sm->errorString() != QString());
505 QVERIFY(!sm->attach());
506 QVERIFY(!sm->detach());
507 } else {
508 QVERIFY(sm->isAttached());
509 }
510 }
511#endif
512}
513
514/*!
515 Create one shared memory (government) and see how many other shared memories (wars) we can
516 attach before the system runs out of resources.
517 */
518// HPUX doesn't allow for multiple attaches per process.
519#if !defined(Q_OS_HPUX)
520void tst_QSharedMemory::attachTooMuch()
521{
522 QSKIP("disabled");
523
524 QSharedMemory government(rememberKey(key: "government"));
525 QVERIFY(government.create(1024));
526 while (true) {
527 QSharedMemory *war = new QSharedMemory(government.key());
528 QVERIFY(war);
529 jail.append(t: war);
530 if (!war->attach()) {
531 QVERIFY(!war->isAttached());
532 QCOMPARE(war->key(), government.key());
533 QCOMPARE(war->size(), 0);
534 QVERIFY(!war->data());
535 QCOMPARE(war->error(), QSharedMemory::OutOfResources);
536 QVERIFY(war->errorString() != QString());
537 QVERIFY(!war->detach());
538 break;
539 } else {
540 QVERIFY(war->isAttached());
541 }
542 }
543}
544#endif
545
546// HPUX doesn't allow for multiple attaches per process.
547#ifndef Q_OS_HPUX
548void tst_QSharedMemory::simpleProducerConsumer_data()
549{
550 QTest::addColumn<QSharedMemory::AccessMode>(name: "mode");
551
552 QTest::newRow(dataTag: "readonly") << QSharedMemory::ReadOnly;
553 QTest::newRow(dataTag: "readwrite") << QSharedMemory::ReadWrite;
554}
555
556/*!
557 The basic consumer producer that rounds out the basic testing.
558 If this fails then any muli-threading/process might fail (but be
559 harder to debug)
560
561 This doesn't require nor test any locking system.
562 */
563void tst_QSharedMemory::simpleProducerConsumer()
564{
565 QFETCH(QSharedMemory::AccessMode, mode);
566
567 rememberKey(key: QLatin1String("market"));
568 QSharedMemory producer(QLatin1String("market"));
569 QSharedMemory consumer(QLatin1String("market"));
570 int size = 512;
571 QVERIFY(producer.create(size));
572 QVERIFY(consumer.attach(mode));
573
574 char *put = (char*)producer.data();
575 char *get = (char*)consumer.data();
576 // On Windows CE you always have ReadWrite access. Thus
577 // ViewMapOfFile returns the same pointer
578 QVERIFY(put != get);
579 for (int i = 0; i < size; ++i) {
580 put[i] = 'Q';
581 QCOMPARE(get[i], 'Q');
582 }
583 QVERIFY(consumer.detach());
584}
585#endif
586
587// HPUX doesn't allow for multiple attaches per process.
588#ifndef Q_OS_HPUX
589void tst_QSharedMemory::simpleDoubleProducerConsumer()
590{
591 rememberKey(key: QLatin1String("market"));
592 QSharedMemory producer(QLatin1String("market"));
593 int size = 512;
594 QVERIFY(producer.create(size));
595 QVERIFY(producer.detach());
596 QVERIFY(producer.create(size));
597
598 {
599 QSharedMemory consumer(QLatin1String("market"));
600 QVERIFY(consumer.attach());
601 }
602}
603#endif
604
605class Consumer : public QThread
606{
607
608public:
609 void run()
610 {
611 QSharedMemory consumer(QLatin1String("market"));
612 while (!consumer.attach()) {
613 if (consumer.error() != QSharedMemory::NotFound)
614 qDebug() << "consumer: failed to connect" << consumer.error() << consumer.errorString();
615 QVERIFY(consumer.error() == QSharedMemory::NotFound || consumer.error() == QSharedMemory::KeyError);
616 QTest::qWait(ms: 1);
617 }
618
619 char *memory = (char*)consumer.data();
620
621 int i = 0;
622 while (true) {
623 if (!consumer.lock())
624 break;
625 if (memory[0] == 'Q')
626 memory[0] = ++i;
627 if (memory[0] == 'E') {
628 memory[1]++;
629 QVERIFY(consumer.unlock());
630 break;
631 }
632 QVERIFY(consumer.unlock());
633 QTest::qWait(ms: 1);
634 }
635
636 QVERIFY(consumer.detach());
637 }
638};
639
640class Producer : public QThread
641{
642
643public:
644 Producer() : producer(QLatin1String("market"))
645 {
646 int size = 1024;
647 if (!producer.create(size)) {
648 // left over from a crash...
649 if (producer.error() == QSharedMemory::AlreadyExists) {
650 producer.attach();
651 producer.detach();
652 QVERIFY(producer.create(size));
653 }
654 }
655 }
656
657 void run()
658 {
659
660 char *memory = (char*)producer.data();
661 memory[1] = '0';
662 QElapsedTimer timer;
663 timer.start();
664 int i = 0;
665 while (i < 5 && timer.elapsed() < 5000) {
666 QVERIFY(producer.lock());
667 if (memory[0] == 'Q') {
668 QVERIFY(producer.unlock());
669 QTest::qWait(ms: 1);
670 continue;
671 }
672 ++i;
673 memory[0] = 'Q';
674 QVERIFY(producer.unlock());
675 QTest::qWait(ms: 1);
676 }
677
678 // tell everyone to quit
679 QVERIFY(producer.lock());
680 memory[0] = 'E';
681 QVERIFY(producer.unlock());
682
683 }
684
685 QSharedMemory producer;
686private:
687
688};
689
690void tst_QSharedMemory::simpleThreadedProducerConsumer_data()
691{
692 QTest::addColumn<bool>(name: "producerIsThread");
693 QTest::addColumn<int>(name: "threads");
694 for (int i = 0; i < 5; ++i) {
695 QTest::newRow(dataTag: "1 consumer, producer is thread") << true << 1;
696 QTest::newRow(dataTag: "1 consumer, producer is this") << false << 1;
697 QTest::newRow(dataTag: "5 consumers, producer is thread") << true << 5;
698 QTest::newRow(dataTag: "5 consumers, producer is this") << false << 5;
699 }
700}
701
702/*!
703 The basic producer/consumer, but this time using threads.
704 */
705void tst_QSharedMemory::simpleThreadedProducerConsumer()
706{
707 QFETCH(bool, producerIsThread);
708 QFETCH(int, threads);
709 rememberKey(key: QLatin1String("market"));
710
711#if defined Q_OS_HPUX && defined __ia64
712 QSKIP("This test locks up on gravlaks.troll.no");
713#endif
714
715 Producer p;
716 QVERIFY(p.producer.isAttached());
717 if (producerIsThread)
718 p.start();
719
720 QList<Consumer*> consumers;
721 for (int i = 0; i < threads; ++i) {
722 consumers.append(t: new Consumer());
723 consumers.last()->start();
724 }
725
726 if (!producerIsThread)
727 p.run();
728
729 p.wait(time: 5000);
730 while (!consumers.isEmpty()) {
731 Consumer *c = consumers.first();
732 QVERIFY(c->isFinished() || c->wait(5000));
733 delete consumers.takeFirst();
734 }
735}
736
737void tst_QSharedMemory::simpleProcessProducerConsumer_data()
738{
739#if QT_CONFIG(process)
740 QTest::addColumn<int>(name: "processes");
741 int tries = 5;
742 for (int i = 0; i < tries; ++i) {
743 QTest::newRow(dataTag: "1 process") << 1;
744 QTest::newRow(dataTag: "5 processes") << 5;
745 }
746#endif
747}
748
749/*!
750 Create external processes that produce and consume.
751 */
752void tst_QSharedMemory::simpleProcessProducerConsumer()
753{
754#if !QT_CONFIG(process)
755 QSKIP("No qprocess support", SkipAll);
756#else
757 QFETCH(int, processes);
758
759 QSKIP("This test is unstable: QTBUG-25655");
760
761 rememberKey(key: "market");
762
763 // Add the executable's directory to path so that we can find the test helper next to it
764 // in a cross-platform way. We must do this because the CWD is not pointing to this directory
765 // in debug-and-release builds.
766 QByteArray path = qgetenv(varName: "PATH");
767 qputenv(varName: "PATH",
768 value: path + QDir::listSeparator().toLatin1()
769 + QCoreApplication::applicationDirPath().toLocal8Bit());
770 auto restore = qScopeGuard(f: [&] { qputenv(varName: "PATH", value: path); });
771
772 QProcess producer;
773 producer.start(program: m_helperBinary, arguments: QStringList("producer"));
774 QVERIFY2(producer.waitForStarted(), "Could not start helper binary");
775 QVERIFY2(producer.waitForReadyRead(), "Helper process failed to create shared memory segment: " +
776 producer.readAllStandardError());
777
778 QList<QProcess*> consumers;
779 unsigned int failedProcesses = 0;
780 const QStringList consumerArguments = QStringList("consumer");
781 for (int i = 0; i < processes; ++i) {
782 QProcess *p = new QProcess;
783 p->setProcessChannelMode(QProcess::ForwardedChannels);
784 p->start(program: m_helperBinary, arguments: consumerArguments);
785 if (p->waitForStarted(msecs: 2000))
786 consumers.append(t: p);
787 else
788 ++failedProcesses;
789 }
790
791 bool consumerFailed = false;
792
793 while (!consumers.isEmpty()) {
794 QVERIFY(consumers.first()->waitForFinished(3000));
795 if (consumers.first()->state() == QProcess::Running ||
796 consumers.first()->exitStatus() != QProcess::NormalExit ||
797 consumers.first()->exitCode() != 0) {
798 consumerFailed = true;
799 }
800 delete consumers.takeFirst();
801 }
802 QCOMPARE(consumerFailed, false);
803 QCOMPARE(failedProcesses, (unsigned int)(0));
804
805 // tell the producer to exit now
806 producer.write(data: "", len: 1);
807 producer.waitForBytesWritten();
808 QVERIFY(producer.waitForFinished(5000));
809#endif
810}
811
812void tst_QSharedMemory::uniqueKey_data()
813{
814 QTest::addColumn<QString>(name: "key1");
815 QTest::addColumn<QString>(name: "key2");
816
817 QTest::newRow(dataTag: "null == null") << QString() << QString();
818 QTest::newRow(dataTag: "key == key") << QString("key") << QString("key");
819 QTest::newRow(dataTag: "key1 == key1") << QString("key1") << QString("key1");
820 QTest::newRow(dataTag: "key != key1") << QString("key") << QString("key1");
821 QTest::newRow(dataTag: "ke1y != key1") << QString("ke1y") << QString("key1");
822 QTest::newRow(dataTag: "key1 != key2") << QString("key1") << QString("key2");
823 QTest::newRow(dataTag: "Noël -> Nol") << QString::fromUtf8(str: "N\xc3\xabl") << QString("Nol");
824}
825
826void tst_QSharedMemory::uniqueKey()
827{
828 QFETCH(QString, key1);
829 QFETCH(QString, key2);
830
831 QSharedMemory sm1(key1);
832 QSharedMemory sm2(key2);
833
834 bool setEqual = (key1 == key2);
835 bool keyEqual = (sm1.key() == sm2.key());
836 bool nativeEqual = (sm1.nativeKey() == sm2.nativeKey());
837
838 QCOMPARE(keyEqual, setEqual);
839 QCOMPARE(nativeEqual, setEqual);
840}
841
842QTEST_MAIN(tst_QSharedMemory)
843#include "tst_qsharedmemory.moc"
844
845

source code of qtbase/tests/auto/corelib/kernel/qsharedmemory/tst_qsharedmemory.cpp