mirror of https://github.com/nymea/nymea.git
Add tests, fix shutdown
parent
746f3e4121
commit
b870140608
|
|
@ -24,8 +24,6 @@ static int PyNymeaLoggingHandler_init(PyNymeaLoggingHandler */*self*/, PyObject
|
|||
static void PyNymeaLoggingHandler_dealloc(PyNymeaLoggingHandler * self)
|
||||
// destruct the object
|
||||
{
|
||||
// FIXME: Why is this not called? Seems we're leaking...
|
||||
Q_ASSERT(false);
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,6 @@ static void PyThing_setThing(PyThing *self, Thing *thing)
|
|||
|
||||
self->pyStates = PyList_New(thing->states().count());
|
||||
for (int i = 0; i < thing->states().count(); i++) {
|
||||
qWarning() << "i" << i;
|
||||
State state = thing->states().at(i);
|
||||
PyObject *pyState = Py_BuildValue("{s:s, s:O}",
|
||||
"stateTypeId", state.stateTypeId().toString().toUtf8().data(),
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include <QtConcurrent/QtConcurrentRun>
|
||||
#include <QCoreApplication>
|
||||
#include <QMutex>
|
||||
#include <QFuture>
|
||||
|
||||
PyThreadState* PythonIntegrationPlugin::s_mainThread = nullptr;
|
||||
PyObject* PythonIntegrationPlugin::s_nymeaModule = nullptr;
|
||||
|
|
@ -71,7 +72,6 @@ static PyModuleDef nymea_module =
|
|||
"nymea module for python based integration plugins", // const char* m_doc;
|
||||
-1, // Py_ssize_t m_size;
|
||||
nymea_methods, // PyMethodDef *m_methods
|
||||
// inquiry m_reload; traverseproc m_traverse; inquiry m_clear; freefunc m_free;
|
||||
nullptr, nullptr, nullptr, nullptr
|
||||
};
|
||||
|
||||
|
|
@ -274,9 +274,16 @@ PythonIntegrationPlugin::PythonIntegrationPlugin(QObject *parent) : IntegrationP
|
|||
|
||||
PythonIntegrationPlugin::~PythonIntegrationPlugin()
|
||||
{
|
||||
PyGILState_STATE s = PyGILState_Ensure();
|
||||
PyGILState_Ensure();
|
||||
|
||||
while (!m_runningThreads.isEmpty()) {
|
||||
PyObject *loop = m_runningThreads.keys().first();
|
||||
PyObject *stop = PyObject_GetAttrString(loop, "stop");
|
||||
PyObject_CallFunctionObjArgs(stop, nullptr);
|
||||
}
|
||||
|
||||
Py_XDECREF(s_plugins.take(this));
|
||||
PyGILState_Release(s);
|
||||
Py_FinalizeEx();
|
||||
}
|
||||
|
||||
void PythonIntegrationPlugin::initPython()
|
||||
|
|
@ -723,7 +730,7 @@ bool PythonIntegrationPlugin::callPluginFunction(const QString &function, PyObje
|
|||
dumpError();
|
||||
|
||||
PyObject *run_until_complete = PyObject_GetAttrString(loop, "run_until_complete");
|
||||
QtConcurrent::run([run_until_complete, task, loop, result](){
|
||||
QFuture<void> future = QtConcurrent::run([this, run_until_complete, task, loop, result](){
|
||||
PyGILState_STATE g = PyGILState_Ensure();
|
||||
// auto s = PyThreadState_New(PyInterpreterState_Main());
|
||||
// PyThreadState *previousThreadState = PyThreadState_Swap(s);
|
||||
|
|
@ -732,11 +739,13 @@ bool PythonIntegrationPlugin::callPluginFunction(const QString &function, PyObje
|
|||
Py_DECREF(task);
|
||||
Py_DECREF(run_until_complete);
|
||||
Py_DECREF(result);
|
||||
m_runningThreads.remove(loop);
|
||||
// PyThreadState_Swap(previousThreadState);
|
||||
// PyThreadState_Clear(s);
|
||||
// PyThreadState_Delete(s);
|
||||
PyGILState_Release(g);
|
||||
});
|
||||
m_runningThreads.insert(loop, future);
|
||||
|
||||
Py_DECREF(create_task);
|
||||
Py_DECREF(add_done_callback);
|
||||
|
|
|
|||
|
|
@ -82,6 +82,8 @@ private:
|
|||
|
||||
// Need to keep a copy of plugin params and sync that in a thread-safe manner
|
||||
ParamList m_pluginConfigCopy;
|
||||
|
||||
QHash<PyObject*, QFuture<void>> m_runningThreads;
|
||||
};
|
||||
|
||||
#endif // PYTHONINTEGRATIONPLUGIN_H
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ async def setupThing(info):
|
|||
|
||||
|
||||
async def postSetupThing(thing):
|
||||
logger.log("postSetupThing for", thing.name, thing.params[0].value)
|
||||
logger.log("postSetupThing for", thing.name)
|
||||
thing.nameChangedHandler = lambda thing : logger.log("Thing name changed", thing.name)
|
||||
|
||||
if thing.thingClassId == pyMockAutoThingClassId:
|
||||
|
|
@ -111,3 +111,8 @@ def autoThings():
|
|||
if thing.thingClassId == pyMockAutoThingClassId:
|
||||
autoThings.append(thing)
|
||||
return autoThings
|
||||
|
||||
|
||||
# Intentionally commented out to also have a test case for unimplmented functions
|
||||
# def thingRemoved(thing):
|
||||
# logger.log("thingRemoved for", thing.name)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ SUBDIRS = \
|
|||
loggingloading \
|
||||
mqttbroker \
|
||||
plugins \
|
||||
pythonplugins \
|
||||
rules \
|
||||
scripts \
|
||||
states \
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
TARGET = testactions
|
||||
|
||||
include(../../../nymea.pri)
|
||||
include(../autotests.pri)
|
||||
|
||||
SOURCES += testpythonplugins.cpp
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2020, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, GNU version 3. This project is distributed in the hope that it
|
||||
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "nymeatestbase.h"
|
||||
|
||||
#include "integrations/thing.h"
|
||||
|
||||
ThingClassId pyMockThingClassId = ThingClassId("1761c256-99b1-41bd-988a-a76087f6a4f1");
|
||||
ThingClassId pyMockDiscoveryPairingThingClassId = ThingClassId("248c5046-847b-44d0-ab7c-684ff79197dc");
|
||||
ParamTypeId pyMockDiscoveryPairingResultCountDiscoveryParamTypeID = ParamTypeId("ef5f6b90-e9d8-4e77-a14d-6725cfb07116");
|
||||
|
||||
using namespace nymeaserver;
|
||||
|
||||
class TestPythonPlugins: public NymeaTestBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
inline void verifyThingError(const QVariant &response, Thing::ThingError error = Thing::ThingErrorNoError) {
|
||||
verifyError(response, "thingError", enumValueName(error));
|
||||
}
|
||||
|
||||
private slots:
|
||||
|
||||
void initTestCase();
|
||||
|
||||
void setupAndRemoveThing();
|
||||
void testDiscoverPairAndRemoveThing();
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
void TestPythonPlugins::initTestCase()
|
||||
{
|
||||
NymeaTestBase::initTestCase();
|
||||
QLoggingCategory::setFilterRules("*.debug=false\n"
|
||||
"Tests.debug=true\n"
|
||||
"PyMock.debug=true\n"
|
||||
);
|
||||
}
|
||||
|
||||
void TestPythonPlugins::setupAndRemoveThing()
|
||||
{
|
||||
QVariantMap resultCountParam;
|
||||
resultCountParam.insert("paramTypeId", pyMockDiscoveryPairingResultCountDiscoveryParamTypeID);
|
||||
resultCountParam.insert("value", 2);
|
||||
|
||||
QVariantList discoveryParams;
|
||||
discoveryParams.append(resultCountParam);
|
||||
|
||||
QVariantMap params;
|
||||
params.insert("thingClassId", pyMockThingClassId);
|
||||
params.insert("name", "Py test thing");
|
||||
QVariant response = injectAndWait("Integrations.AddThing", params);
|
||||
|
||||
verifyThingError(response, Thing::ThingErrorNoError);
|
||||
ThingId thingId = response.toMap().value("params").toMap().value("thingId").toUuid();
|
||||
qCDebug(dcTests()) << "New thing id" << thingId;
|
||||
|
||||
params.clear();
|
||||
params.insert("thingId", thingId);
|
||||
injectAndWait("Integrations.RemoveThing", params);
|
||||
verifyThingError(response, Thing::ThingErrorNoError);
|
||||
}
|
||||
|
||||
void TestPythonPlugins::testDiscoverPairAndRemoveThing()
|
||||
{
|
||||
// Discover
|
||||
QVariantMap resultCountParam;
|
||||
resultCountParam.insert("paramTypeId", pyMockDiscoveryPairingResultCountDiscoveryParamTypeID);
|
||||
resultCountParam.insert("value", 2);
|
||||
|
||||
QVariantList discoveryParams;
|
||||
discoveryParams.append(resultCountParam);
|
||||
|
||||
QVariantMap params;
|
||||
params.insert("thingClassId", pyMockDiscoveryPairingThingClassId);
|
||||
params.insert("discoveryParams", discoveryParams);
|
||||
QVariant response = injectAndWait("Integrations.DiscoverThings", params);
|
||||
|
||||
verifyThingError(response, Thing::ThingErrorNoError);
|
||||
QCOMPARE(response.toMap().value("params").toMap().value("thingDescriptors").toList().count(), 2);
|
||||
|
||||
ThingDescriptorId descriptorId = response.toMap().value("params").toMap().value("thingDescriptors").toList().first().toMap().value("id").toUuid();
|
||||
|
||||
// Pair
|
||||
params.clear();
|
||||
params.insert("thingDescriptorId", descriptorId);
|
||||
response = injectAndWait("Integrations.PairThing", params);
|
||||
verifyThingError(response, Thing::ThingErrorNoError);
|
||||
|
||||
qWarning() << "respo" << response.toMap().value("params").toMap();
|
||||
PairingTransactionId transactionId = response.toMap().value("params").toMap().value("pairingTransactionId").toUuid();
|
||||
qWarning() << "transactionId" << transactionId;
|
||||
|
||||
params.clear();
|
||||
params.insert("pairingTransactionId", transactionId);
|
||||
params.insert("username", "john");
|
||||
params.insert("secret", "smith");
|
||||
response = injectAndWait("Integrations.ConfirmPairing", params);
|
||||
verifyThingError(response, Thing::ThingErrorNoError);
|
||||
ThingId thingId = response.toMap().value("params").toMap().value("thingId").toUuid();
|
||||
|
||||
// Remove
|
||||
params.clear();
|
||||
params.insert("thingId", thingId);
|
||||
response = injectAndWait("Integrations.RemoveThing", params);
|
||||
verifyThingError(response, Thing::ThingErrorNoError);
|
||||
}
|
||||
|
||||
#include "testpythonplugins.moc"
|
||||
QTEST_MAIN(TestPythonPlugins)
|
||||
Loading…
Reference in New Issue