File indexing completed on 2024-05-12 05:26:26
0001 #include <QTest> 0002 0003 #include "dummyresource/resourcefactory.h" 0004 #include "resourcecontrol.h" 0005 #include "store.h" 0006 #include "test.h" 0007 #include "resourceconfig.h" 0008 #include "resourceaccess.h" 0009 #include "commands.h" 0010 0011 /** 0012 * Test starting and stopping of resources. 0013 */ 0014 class ResourceControlTest : public QObject 0015 { 0016 Q_OBJECT 0017 0018 KAsync::Job<bool> socketIsAvailable(const QByteArray &identifier) 0019 { 0020 return Sink::ResourceAccess::connectToServer(identifier) 0021 .then<void, QSharedPointer<QLocalSocket>>( 0022 [&](const KAsync::Error &error, QSharedPointer<QLocalSocket> socket) { 0023 if (error) { 0024 return KAsync::value(false); 0025 } 0026 socket->close(); 0027 return KAsync::value(true); 0028 }); 0029 0030 } 0031 0032 bool blockingSocketIsAvailable(const QByteArray &identifier) 0033 { 0034 auto job = socketIsAvailable(identifier); 0035 auto future = job.exec(); 0036 future.waitForFinished(); 0037 return future.value(); 0038 } 0039 0040 private slots: 0041 0042 void initTestCase() 0043 { 0044 Sink::Test::initTest(); 0045 auto factory = Sink::ResourceFactory::load("sink.dummy"); 0046 QVERIFY(factory); 0047 ::DummyResource::removeFromDisk("sink.dummy.instance1"); 0048 ResourceConfig::addResource("sink.dummy.instance1", "sink.dummy"); 0049 ::DummyResource::removeFromDisk("sink.dummy.instance2"); 0050 ResourceConfig::addResource("sink.dummy.instance2", "sink.dummy"); 0051 } 0052 0053 void testResourceStart() 0054 { 0055 VERIFYEXEC(Sink::ResourceControl::start("sink.dummy.instance1")); 0056 QVERIFY(blockingSocketIsAvailable("sink.dummy.instance1")); 0057 } 0058 0059 void testResourceShutdown() 0060 { 0061 QVERIFY(!blockingSocketIsAvailable("sink.dummy.instance2")); 0062 VERIFYEXEC(Sink::ResourceControl::start("sink.dummy.instance2")); 0063 QVERIFY(blockingSocketIsAvailable("sink.dummy.instance2")); 0064 VERIFYEXEC(Sink::ResourceControl::shutdown("sink.dummy.instance2")); 0065 QVERIFY(!blockingSocketIsAvailable("sink.dummy.instance2")); 0066 } 0067 0068 //This will produce a race where the synchronize command starts the resource, 0069 //the shutdown command doesn't shutdown because it doesn't realize that the resource is up, 0070 //and the resource ends up getting started, but doing nothing. 0071 void testResourceShutdownAfterStartByCommand() 0072 { 0073 QVERIFY(!blockingSocketIsAvailable("sink.dummy.instance2")); 0074 auto future = Sink::Store::synchronize(Sink::SyncScope{}.resourceFilter("sink.dummy.instance2")).exec(); 0075 0076 VERIFYEXEC(Sink::ResourceControl::shutdown("sink.dummy.instance2")); 0077 0078 QVERIFY(!blockingSocketIsAvailable("sink.dummy.instance2")); 0079 } 0080 0081 /** 0082 * An existing live-query should not restart the resource due to revisionReplayedCommands. 0083 * This was introduced for tests, in regular use the resources are running during the whole query anyways, 0084 * because a live query will start the resource via an explicit call to open(). 0085 */ 0086 void testRevisionReplayedAfterShutdown() 0087 { 0088 //Prepare 0089 const QByteArray identifier{"sink.dummy.instance2"}; 0090 QVERIFY(!blockingSocketIsAvailable(identifier)); 0091 VERIFYEXEC(Sink::ResourceControl::start(identifier)); 0092 QVERIFY(blockingSocketIsAvailable(identifier)); 0093 auto resourceAccess = Sink::ResourceAccessFactory::instance().getAccess(identifier, ResourceConfig::getResourceType(identifier)); 0094 0095 //Shutdown and immediately send a revision replayed command 0096 VERIFYEXEC(Sink::ResourceControl::shutdown(identifier)); 0097 VERIFYEXEC_FAIL(resourceAccess->sendRevisionReplayedCommand(1)); 0098 0099 //This should not start the resource again 0100 QVERIFY(!blockingSocketIsAvailable("sink.dummy.instance2")); 0101 } 0102 0103 void testAbortCommandsOnShutdown() 0104 { 0105 const QByteArray identifier{"sink.dummy.instance1"}; 0106 VERIFYEXEC(Sink::ResourceControl::shutdown(identifier)); 0107 auto resourceAccess = Sink::ResourceAccessFactory::instance().getAccess(identifier, ResourceConfig::getResourceType(identifier)); 0108 resourceAccess->shutdown().exec(); 0109 //This operation should be aborted by the shutdown operation 0110 VERIFYEXEC_FAIL(Sink::ResourceControl::start(identifier)); 0111 } 0112 0113 void testResourceShutdownRestartLoop() 0114 { 0115 const QByteArray identifier{"sink.dummy.instance1"}; 0116 VERIFYEXEC(Sink::ResourceControl::shutdown(identifier)); 0117 QVERIFY(!blockingSocketIsAvailable(identifier)); 0118 for (int i = 0; i < 10; i++) { 0119 Sink::ResourceControl::start(identifier).exec().waitForFinished(); 0120 Sink::ResourceControl::shutdown(identifier).exec().waitForFinished(); 0121 } 0122 QVERIFY(!blockingSocketIsAvailable(identifier)); 0123 } 0124 0125 /** 0126 * This test used to trigger a SIGPIPE, before we started to abort the socket on shutdown. 0127 */ 0128 void testResourceShutdownRestartWithCommandLoop() 0129 { 0130 const QByteArray identifier{"sink.dummy.instance1"}; 0131 VERIFYEXEC(Sink::ResourceControl::shutdown(identifier)); 0132 QVERIFY(!blockingSocketIsAvailable(identifier)); 0133 for (int i = 0; i < 10; i++) { 0134 auto resourceAccess = Sink::ResourceAccessFactory::instance().getAccess(identifier, ResourceConfig::getResourceType(identifier)); 0135 resourceAccess->sendCommand(Sink::Commands::PingCommand).exec(); 0136 resourceAccess->shutdown().exec().waitForFinished(); 0137 Sink::ResourceControl::start(identifier).exec().waitForFinished(); 0138 } 0139 0140 VERIFYEXEC(Sink::ResourceControl::shutdown(identifier)); 0141 QVERIFY(!blockingSocketIsAvailable(identifier)); 0142 } 0143 0144 /** 0145 * This seems to somehow corrupt the stack and crashes with 0146 * malloc(): unaligned tcache chunk detected 0147 */ 0148 void testResourceShutdownCrash() 0149 { 0150 QSKIP("Results in a crash"); 0151 const QByteArray identifier{"sink.dummy.instance1"}; 0152 VERIFYEXEC(Sink::ResourceControl::shutdown(identifier)); 0153 QVERIFY(!blockingSocketIsAvailable(identifier)); 0154 { 0155 auto resourceAccess = Sink::ResourceAccessFactory::instance().getAccess(identifier, ResourceConfig::getResourceType(identifier)); 0156 QTest::qWait(500); 0157 resourceAccess->shutdown().exec().waitForFinished(); 0158 } 0159 Sink::ResourceControl::start(identifier).exec().waitForFinished(); 0160 0161 VERIFYEXEC(Sink::ResourceControl::shutdown(identifier)); 0162 QVERIFY(!blockingSocketIsAvailable(identifier)); 0163 } 0164 0165 }; 0166 0167 QTEST_MAIN(ResourceControlTest) 0168 #include "resourcecontroltest.moc"