|
@@ -0,0 +1,495 @@
|
|
|
+/*=============================================================================
|
|
|
+
|
|
|
+ Library: CTK
|
|
|
+
|
|
|
+ Copyright (c) 2010 German Cancer Research Center,
|
|
|
+ Division of Medical and Biological Informatics
|
|
|
+
|
|
|
+ Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
+ you may not use this file except in compliance with the License.
|
|
|
+ You may obtain a copy of the License at
|
|
|
+
|
|
|
+ http://www.apache.org/licenses/LICENSE-2.0
|
|
|
+
|
|
|
+ Unless required by applicable law or agreed to in writing, software
|
|
|
+ distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
+ See the License for the specific language governing permissions and
|
|
|
+ limitations under the License.
|
|
|
+
|
|
|
+=============================================================================*/
|
|
|
+
|
|
|
+#include "ctkPluginFrameworkTestSuite_p.h"
|
|
|
+
|
|
|
+#include <ctkPluginFrameworkTestUtil.h>
|
|
|
+#include <ctkPluginContext.h>
|
|
|
+#include <ctkPluginConstants.h>
|
|
|
+#include <ctkServiceException.h>
|
|
|
+
|
|
|
+#include <QTest>
|
|
|
+#include <QDebug>
|
|
|
+
|
|
|
+
|
|
|
+int ctkPluginFrameworkTestSuite::nRunCount = 0;
|
|
|
+
|
|
|
+
|
|
|
+ctkPluginFrameworkTestSuite::ctkPluginFrameworkTestSuite(ctkPluginContext* pc)
|
|
|
+ : eventDelay(500), pc(pc), p(pc->getPlugin())
|
|
|
+{
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+void ctkPluginFrameworkTestSuite::initTestCase()
|
|
|
+{
|
|
|
+ qDebug() << "### plugin framework test suite: SETUP start";
|
|
|
+ if (nRunCount > 0)
|
|
|
+ {
|
|
|
+ QFAIL("The ctkPluginFrameworkTestSuite CANNOT be run reliably more than once. Other test results in this suite are/may not be valid. Restart framework to retest: CLEANUP:FAIL");
|
|
|
+ }
|
|
|
+ ++nRunCount;
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ bool success = pc->connectFrameworkListener(this, SLOT(frameworkListener(ctkPluginFrameworkEvent)));
|
|
|
+ if (!success)
|
|
|
+ {
|
|
|
+ QFAIL("plugin framework test suite: SETUP:FAIL");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (const std::logic_error& e)
|
|
|
+ {
|
|
|
+ QString msg = QString("plugin framework test suite ") + e.what() + ": SETUP:FAIL";
|
|
|
+ QFAIL(msg.toAscii());
|
|
|
+ }
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ bool success = pc->connectPluginListener(this, SLOT(pluginListener(ctkPluginEvent)));
|
|
|
+ if (!success)
|
|
|
+ {
|
|
|
+ QFAIL("plugin framework test suite: SETUP:FAIL");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (const std::logic_error& e)
|
|
|
+ {
|
|
|
+ QString msg = QString("plugin framework test suite ") + e.what() + ": SETUP:FAIL";
|
|
|
+ QFAIL(msg.toAscii());
|
|
|
+ }
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ bool success = pc->connectPluginListener(this, SLOT(syncPluginListener(ctkPluginEvent)), Qt::DirectConnection);
|
|
|
+ if (!success)
|
|
|
+ {
|
|
|
+ QFAIL("plugin framework test suite: SETUP:FAIL");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (const std::logic_error& e)
|
|
|
+ {
|
|
|
+ QString msg = QString("plugin framework test suite ") + e.what() + ": SETUP:FAIL";
|
|
|
+ QFAIL(msg.toAscii());
|
|
|
+ }
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ pc->connectServiceListener(this, "serviceListener");
|
|
|
+ }
|
|
|
+ catch (const std::logic_error& e)
|
|
|
+ {
|
|
|
+ QString msg = QString("plugin framework test suite ") + e.what() + ": SETUP:FAIL";
|
|
|
+ QFAIL(msg.toAscii());
|
|
|
+ }
|
|
|
+
|
|
|
+ qDebug() << "### plugin framework test suite: SETUP:PASS";
|
|
|
+}
|
|
|
+
|
|
|
+void ctkPluginFrameworkTestSuite::cleanupTestCase()
|
|
|
+{
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// Verify information from the getHeaders() method
|
|
|
+void ctkPluginFrameworkTestSuite::frame005a()
|
|
|
+{
|
|
|
+ QHash<QString, QString> headers = p->getHeaders();
|
|
|
+
|
|
|
+ // check expected headers
|
|
|
+
|
|
|
+ QString k = "Plugin-Name";
|
|
|
+ QCOMPARE(QString("framework_test"), headers.value(k));
|
|
|
+
|
|
|
+ k = "Plugin-Version";
|
|
|
+ QCOMPARE(QString("1.0.1"), headers.value(k));
|
|
|
+
|
|
|
+ k = "Plugin-Description";
|
|
|
+ QCOMPARE(QString("Test bundle for the CTK plugin framework"), headers.value(k));
|
|
|
+
|
|
|
+ k = "Plugin-Vendor";
|
|
|
+ QCOMPARE(QString("CommonTK"), headers.value(k));
|
|
|
+
|
|
|
+ k = "Plugin-DocURL";
|
|
|
+ QCOMPARE(QString("http://www.commontk.org"), headers.value(k));
|
|
|
+
|
|
|
+ k = "Plugin-ContactAddress";
|
|
|
+ QCOMPARE(QString("http://www.commontk.org"), headers.value(k));
|
|
|
+
|
|
|
+ k = "Plugin-Category";
|
|
|
+ QCOMPARE(QString("test"), headers.value(k));
|
|
|
+
|
|
|
+ k = "Plugin-Copyright";
|
|
|
+ QCOMPARE(QString("German Cancer Research Center, Division of Medical and Biological Informatics"), headers.value(k));
|
|
|
+
|
|
|
+ k = "Plugin-License";
|
|
|
+ QCOMPARE(QString("http://www.apache.org/licenses/LICENSE-2.0.html"), headers.value(k));
|
|
|
+}
|
|
|
+
|
|
|
+// Load pluginA_test and check that it exists and that its expected service does not exist,
|
|
|
+// also check that the expected events in the framework occur
|
|
|
+void ctkPluginFrameworkTestSuite::frame020a()
|
|
|
+{
|
|
|
+ pA = 0;
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ pA = ctkPluginFrameworkTestUtil::installPlugin(pc, "pluginA_test");
|
|
|
+ }
|
|
|
+ catch (ctkPluginException& e)
|
|
|
+ {
|
|
|
+ QFAIL(e.what());
|
|
|
+ }
|
|
|
+
|
|
|
+ const QHash<QString, QString> headers = pA->getHeaders();
|
|
|
+ QHash<QString, QString>::const_iterator iter =
|
|
|
+ headers.find(ctkPluginConstants::PLUGIN_SYMBOLICNAME);
|
|
|
+ QVERIFY(iter != headers.end());
|
|
|
+ QCOMPARE(iter.value(), QString("pluginA.test"));
|
|
|
+
|
|
|
+ // Check that no service reference exist yet.
|
|
|
+ try
|
|
|
+ {
|
|
|
+ pc->getServiceReference("ctkTestPluginAService");
|
|
|
+ QFAIL("framework test plugin, service from test plugin A unexpectedly found");
|
|
|
+ }
|
|
|
+ catch (ctkServiceException& e)
|
|
|
+ {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // check the listeners for events, expect only a plugin event,
|
|
|
+ // of type installation
|
|
|
+ bool lStat = checkListenerEvents(false, ctkPluginFrameworkEvent::INFO, true , ctkPluginEvent::INSTALLED,
|
|
|
+ false, ctkServiceEvent::MODIFIED, pA, 0);
|
|
|
+
|
|
|
+ QVERIFY(pA->getState() == ctkPlugin::INSTALLED && lStat == true);
|
|
|
+}
|
|
|
+
|
|
|
+// Start pluginA_test and check that it gets state ACTIVE,
|
|
|
+// and that the service it registers exist
|
|
|
+void ctkPluginFrameworkTestSuite::frame025b()
|
|
|
+{
|
|
|
+ try
|
|
|
+ {
|
|
|
+ pA->start();
|
|
|
+ QVERIFY2(pA->getState() == ctkPlugin::ACTIVE, "pluginA_test should be ACTIVE");
|
|
|
+ }
|
|
|
+ catch (const ctkPluginException& pexcA)
|
|
|
+ {
|
|
|
+ QString failMsg = QString("Unexpected plugin exception: ") + pexcA.what();
|
|
|
+ QFAIL(failMsg.toStdString().c_str());
|
|
|
+ }
|
|
|
+ catch (const std::logic_error& le)
|
|
|
+ {
|
|
|
+ QString failMsg = QString("Start plugin exception: ") + le.what();
|
|
|
+ QFAIL(failMsg.toStdString().c_str());
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check if pluginA_test registered the expected service
|
|
|
+ try
|
|
|
+ {
|
|
|
+ ctkServiceReference sr1 = pc->getServiceReference("ctkTestPluginAService");
|
|
|
+ QObject* o1 = pc->getService(sr1);
|
|
|
+ QVERIFY2(o1 != 0, "no service object found");
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ QVERIFY2(pc->ungetService(sr1), "Service unget should return true");
|
|
|
+ }
|
|
|
+ catch (const std::logic_error le)
|
|
|
+ {
|
|
|
+ QString failMsg = QString("Unget service exception: ") + le.what();
|
|
|
+ QFAIL(failMsg.toStdString().c_str());
|
|
|
+ }
|
|
|
+
|
|
|
+ // check the listeners for events
|
|
|
+ QList<ctkPluginEvent> pEvts;
|
|
|
+ pEvts.push_back(ctkPluginEvent(ctkPluginEvent::RESOLVED, pA));
|
|
|
+ pEvts.push_back(ctkPluginEvent(ctkPluginEvent::STARTED, pA));
|
|
|
+
|
|
|
+ QList<ctkServiceEvent> seEvts;
|
|
|
+ seEvts.push_back(ctkServiceEvent(ctkServiceEvent::REGISTERED, sr1));
|
|
|
+
|
|
|
+ QVERIFY2(checkListenerEvents(QList<ctkPluginFrameworkEvent>(), pEvts, seEvts),
|
|
|
+ "Unexpected events");
|
|
|
+
|
|
|
+ QList<ctkPluginEvent> syncPEvts;
|
|
|
+ syncPEvts.push_back(ctkPluginEvent(ctkPluginEvent::STARTING, pA));
|
|
|
+
|
|
|
+ QVERIFY2(checkSyncListenerEvents(syncPEvts), "Unexpected events");
|
|
|
+ }
|
|
|
+ catch (const ctkServiceException& /*se*/)
|
|
|
+ {
|
|
|
+ QFAIL("framework test bundle, expected service not found");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void ctkPluginFrameworkTestSuite::frameworkListener(const ctkPluginFrameworkEvent& fwEvent)
|
|
|
+{
|
|
|
+ frameworkEvents.push_back(fwEvent);
|
|
|
+ qDebug() << "FrameworkEvent:" << fwEvent;
|
|
|
+}
|
|
|
+
|
|
|
+void ctkPluginFrameworkTestSuite::pluginListener(const ctkPluginEvent& event)
|
|
|
+{
|
|
|
+ pluginEvents.push_back(event);
|
|
|
+ qDebug() << "PluginEvent:" << event;
|
|
|
+}
|
|
|
+
|
|
|
+void ctkPluginFrameworkTestSuite::syncPluginListener(const ctkPluginEvent& event)
|
|
|
+{
|
|
|
+ if (event.getType() == ctkPluginEvent::STARTING ||
|
|
|
+ event.getType() == ctkPluginEvent::STOPPING)
|
|
|
+ {
|
|
|
+ syncPluginEvents.push_back(event);
|
|
|
+ qDebug() << "Synchronous PluginEvent:" << event;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void ctkPluginFrameworkTestSuite::serviceListener(const ctkServiceEvent& event)
|
|
|
+{
|
|
|
+ serviceEvents.push_back(event);
|
|
|
+ qDebug() << "ServiceEvent:" << event;
|
|
|
+}
|
|
|
+
|
|
|
+ctkPluginEvent ctkPluginFrameworkTestSuite::getPluginEvent() const
|
|
|
+{
|
|
|
+ if (pluginEvents.empty())
|
|
|
+ {
|
|
|
+ return ctkPluginEvent();
|
|
|
+ }
|
|
|
+ return pluginEvents.last();
|
|
|
+}
|
|
|
+
|
|
|
+ctkPluginEvent ctkPluginFrameworkTestSuite::getSyncPluginEvent() const
|
|
|
+{
|
|
|
+ if (syncPluginEvents.empty())
|
|
|
+ {
|
|
|
+ return ctkPluginEvent();
|
|
|
+ }
|
|
|
+ return syncPluginEvents.last();
|
|
|
+}
|
|
|
+
|
|
|
+ctkPluginFrameworkEvent ctkPluginFrameworkTestSuite::getFrameworkEvent() const
|
|
|
+{
|
|
|
+ if (frameworkEvents.empty())
|
|
|
+ {
|
|
|
+ return ctkPluginFrameworkEvent();
|
|
|
+ }
|
|
|
+ return frameworkEvents.last();
|
|
|
+}
|
|
|
+
|
|
|
+ctkServiceEvent ctkPluginFrameworkTestSuite::getServiceEvent() const
|
|
|
+{
|
|
|
+ if (serviceEvents.empty())
|
|
|
+ {
|
|
|
+ return ctkServiceEvent();
|
|
|
+ }
|
|
|
+ return serviceEvents.last();
|
|
|
+}
|
|
|
+
|
|
|
+bool ctkPluginFrameworkTestSuite::checkListenerEvents(
|
|
|
+ bool fwexp, ctkPluginFrameworkEvent::Type fwtype,
|
|
|
+ bool pexp, ctkPluginEvent::Type ptype,
|
|
|
+ bool sexp, ctkServiceEvent::Type stype,
|
|
|
+ ctkPlugin* pluginX, ctkServiceReference* servX)
|
|
|
+{
|
|
|
+ QList<ctkPluginFrameworkEvent> fwEvts;
|
|
|
+ QList<ctkPluginEvent> pEvts;
|
|
|
+ QList<ctkServiceEvent> seEvts;
|
|
|
+
|
|
|
+ if (fwexp) fwEvts << ctkPluginFrameworkEvent(fwtype, pluginX);
|
|
|
+ if (pexp) pEvts << ctkPluginEvent(ptype, pluginX);
|
|
|
+ if (sexp) seEvts << ctkServiceEvent(stype, *servX);
|
|
|
+
|
|
|
+ return checkListenerEvents(fwEvts, pEvts, seEvts);
|
|
|
+}
|
|
|
+
|
|
|
+bool ctkPluginFrameworkTestSuite::checkListenerEvents(
|
|
|
+ const QList<ctkPluginFrameworkEvent>& fwEvts,
|
|
|
+ const QList<ctkPluginEvent>& pEvts,
|
|
|
+ const QList<ctkServiceEvent>& seEvts)
|
|
|
+{
|
|
|
+ bool listenState = true; // assume everything will work
|
|
|
+
|
|
|
+ // Wait a while to allow events to arrive
|
|
|
+ QTest::qWait(eventDelay);
|
|
|
+ //QCoreApplication::sendPostedEvents();
|
|
|
+
|
|
|
+ if (fwEvts.size() != frameworkEvents.size())
|
|
|
+ {
|
|
|
+ listenState = false;
|
|
|
+ qDebug() << "*** Plugin Framework event mismatch: expected"
|
|
|
+ << fwEvts.size() << "event(s), found"
|
|
|
+ << frameworkEvents.size() << "event(s).";
|
|
|
+
|
|
|
+ const int max = fwEvts.size() > frameworkEvents.size()
|
|
|
+ ? fwEvts.size() : frameworkEvents.size();
|
|
|
+ for (int i = 0; i < max; ++i)
|
|
|
+ {
|
|
|
+ const ctkPluginFrameworkEvent& fwE = i < fwEvts.size() ? fwEvts[i] : ctkPluginFrameworkEvent();
|
|
|
+ const ctkPluginFrameworkEvent& fwR = i < frameworkEvents.size() ? frameworkEvents[i] : ctkPluginFrameworkEvent();
|
|
|
+ qDebug() << " " << fwE << " - " << fwR;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ for (int i = 0; i < fwEvts.size(); ++i)
|
|
|
+ {
|
|
|
+ const ctkPluginFrameworkEvent& feE = fwEvts[i];
|
|
|
+ const ctkPluginFrameworkEvent& feR = frameworkEvents[i];
|
|
|
+ if (feE.getType() != feR.getType()
|
|
|
+ || feE.getPlugin() != feR.getPlugin())
|
|
|
+ {
|
|
|
+ listenState = false;
|
|
|
+ qDebug() << "*** Wrong framework event:" << feR
|
|
|
+ << "expected" << feE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pEvts.size() != pluginEvents.size())
|
|
|
+ {
|
|
|
+ listenState = false;
|
|
|
+ qDebug() << "*** Plugin event mismatch: expected"
|
|
|
+ << pEvts.size() << "event(s), found "
|
|
|
+ << pluginEvents.size() << "event(s).";
|
|
|
+
|
|
|
+ const int max = pEvts.size() > pluginEvents.size() ? pEvts.size() : pluginEvents.size();
|
|
|
+ for (int i = 0; i < max; ++i)
|
|
|
+ {
|
|
|
+ const ctkPluginEvent& pE = i < pEvts.size() ? pEvts[i] : ctkPluginEvent();
|
|
|
+ const ctkPluginEvent& pR = i < pluginEvents.size() ? pluginEvents[i] : ctkPluginEvent();
|
|
|
+ qDebug() << " " << pE << " - " << pR;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ for (int i = 0; i < pEvts.size(); ++i)
|
|
|
+ {
|
|
|
+ const ctkPluginEvent& pE = pEvts[i];
|
|
|
+ const ctkPluginEvent& pR = pluginEvents[i];
|
|
|
+ if (pE.getType() != pR.getType()
|
|
|
+ || pE.getPlugin() != pR.getPlugin())
|
|
|
+ {
|
|
|
+ listenState = false;
|
|
|
+ qDebug() << "*** Wrong plugin event:" << pR << "expected" << pE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (seEvts.size() != serviceEvents.size())
|
|
|
+ {
|
|
|
+ listenState = false;
|
|
|
+ qDebug() << "*** Service event mismatch: expected"
|
|
|
+ << seEvts.size() << "event(s), found"
|
|
|
+ << serviceEvents.size() << "event(s).";
|
|
|
+
|
|
|
+ const int max = seEvts.size() > serviceEvents.size()
|
|
|
+ ? seEvts.size() : serviceEvents.size();
|
|
|
+ for (int i = 0; i < max; ++i)
|
|
|
+ {
|
|
|
+ const ctkServiceEvent& seE = i < seEvts.size() ? seEvts[i] : ctkServiceEvent();
|
|
|
+ const ctkServiceEvent& seR = i < serviceEvents.size() ? serviceEvents[i] : ctkServiceEvent();
|
|
|
+ qDebug() << " " << seE << " - " << seR;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ for (int i = 0; i < seEvts.size(); ++i)
|
|
|
+ {
|
|
|
+ const ctkServiceEvent& seE = seEvts[i];
|
|
|
+ const ctkServiceEvent& seR = serviceEvents[i];
|
|
|
+ if (seE.getType() != seR.getType()
|
|
|
+ || (!(seE.getServiceReference() == seR.getServiceReference())))
|
|
|
+ {
|
|
|
+ listenState = false;
|
|
|
+ qDebug() << "*** Wrong service event:" << seR << "expected" << seE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ frameworkEvents.clear();
|
|
|
+ pluginEvents.clear();
|
|
|
+ serviceEvents.clear();
|
|
|
+ return listenState;
|
|
|
+}
|
|
|
+
|
|
|
+// Check that the expected events have reached the listeners and
|
|
|
+// reset the events
|
|
|
+bool ctkPluginFrameworkTestSuite::checkSyncListenerEvents(
|
|
|
+ bool pexp, ctkPluginEvent::Type ptype, ctkPlugin* pX,
|
|
|
+ ctkServiceReference servX)
|
|
|
+{
|
|
|
+ Q_UNUSED(servX)
|
|
|
+
|
|
|
+ QList<ctkPluginEvent> pEvts;
|
|
|
+
|
|
|
+ if (pexp)
|
|
|
+ {
|
|
|
+ pEvts << ctkPluginEvent(ptype, pX);
|
|
|
+ }
|
|
|
+
|
|
|
+ return checkSyncListenerEvents(pEvts);
|
|
|
+}
|
|
|
+
|
|
|
+// Check that the expected events have reached the listeners and
|
|
|
+// reset the events
|
|
|
+bool ctkPluginFrameworkTestSuite::checkSyncListenerEvents(
|
|
|
+ const QList<ctkPluginEvent>& pEvts)
|
|
|
+{
|
|
|
+ bool listenState = true; // assume everything will work
|
|
|
+
|
|
|
+ // Sleep a while to allow events to arrive
|
|
|
+ QTest::qWait(eventDelay);
|
|
|
+
|
|
|
+ if (pEvts.size() != syncPluginEvents.size())
|
|
|
+ {
|
|
|
+ listenState = false;
|
|
|
+ qDebug() << "*** Sync plugin event mismatch: expected"
|
|
|
+ << pEvts.size() << "event(s), found"
|
|
|
+ << syncPluginEvents.size() << "event(s).";
|
|
|
+
|
|
|
+ const int max = pEvts.size() > syncPluginEvents.size() ? pEvts.size() : syncPluginEvents.size();
|
|
|
+ for (int i = 0; i < max; ++i)
|
|
|
+ {
|
|
|
+ const ctkPluginEvent& pE = i< pEvts.size() ? pEvts[i] : ctkPluginEvent();
|
|
|
+ const ctkPluginEvent& pR = i < syncPluginEvents.size() ? syncPluginEvents[i] : ctkPluginEvent();
|
|
|
+ qDebug() << " " << pE << " - " << pR;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ for (int i = 0; i < pEvts.size(); ++i)
|
|
|
+ {
|
|
|
+ const ctkPluginEvent& pE = pEvts[i];
|
|
|
+ const ctkPluginEvent& pR = syncPluginEvents[i];
|
|
|
+ if (pE.getType() != pR.getType() || pE.getPlugin() != pR.getPlugin())
|
|
|
+ {
|
|
|
+ listenState = false;
|
|
|
+ qDebug() << "Wrong sync plugin event:" << pR << "expected" << pE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ syncPluginEvents.clear();
|
|
|
+ return listenState;
|
|
|
+}
|