Fixes and tests.

This commit is contained in:
Mikaël Capelle
2022-05-02 18:56:44 +02:00
parent 6ea9d92b26
commit 30a01cf783
15 changed files with 308 additions and 51 deletions
-1
View File
@@ -24,7 +24,6 @@ mo2_python_pip_install(pytest
PACKAGES pytest PyQt6==6.3.0)
add_dependencies(pytest mobase)
file(GLOB test_files CONFIGURE_DEPENDS "test_*.cpp")
foreach (test_file ${test_files})
get_filename_component(target ${test_file} NAME_WLE)
+60
View File
@@ -2,6 +2,8 @@
#include <pybind11/pybind11.h>
#include <tuple>
using namespace pybind11::literals;
PYBIND11_MODULE(qt, m)
@@ -33,6 +35,7 @@ PYBIND11_MODULE(qt, m)
});
// QMap
m.def("qmap_to_length", [](QMap<QString, QString> const& map) {
QMap<QString, int> res;
for (auto it = map.begin(); it != map.end(); ++it) {
@@ -56,4 +59,61 @@ PYBIND11_MODULE(qt, m)
return datetime.toString(format);
},
"datetime"_a, "format"_a = Qt::DateFormat::ISODate);
// QVariant
m.def("qvariant_from_none", [](QVariant const& variant) {
return std::make_tuple(variant.userType() == QVariant::Invalid,
variant.isValid());
});
m.def("qvariant_from_int", [](QVariant const& variant) {
return std::make_tuple(variant.userType() == QVariant::Int, variant.toInt());
});
m.def("qvariant_from_bool", [](QVariant const& variant) {
return std::make_tuple(variant.userType() == QVariant::Bool, variant.toBool());
});
m.def("qvariant_from_str", [](QVariant const& variant) {
return std::make_tuple(variant.userType() == QVariant::String,
variant.toString());
});
m.def("qvariant_from_list", [](QVariant const& variant) {
return std::make_tuple(variant.userType() == QVariant::List, variant.toList());
});
m.def("qvariant_from_map", [](QVariant const& variant) {
return std::make_tuple(variant.userType() == QVariant::Map, variant.toMap());
});
m.def("qvariant_none", []() {
return QVariant();
});
m.def("qvariant_int", []() {
return QVariant(42);
});
m.def("qvariant_bool", []() {
return QVariant(true);
});
m.def("qvariant_str", []() {
return QVariant("hello world");
});
m.def("qvariant_list", [] {
QVariantMap subMap;
subMap["bar"] = 42;
subMap["moo"] = QVariantList{44, true};
QVariantList list;
list.push_back(33);
list.push_back(QVariantList{4, "foo"});
list.push_back(false);
list.push_back("hello");
list.push_back(QVariant());
list.push_back(subMap);
list.push_back(45);
return QVariant(list);
});
m.def("qvariant_map", []() {
QVariantMap map;
map["bar"] = 42;
map["moo"] = true;
map["baz"] = "world hello";
return map;
});
}
+43 -1
View File
@@ -1,6 +1,5 @@
import pytest
from PyQt6.QtCore import QDateTime, Qt
from PyQt6.QtWidgets import QWidget
m = pytest.importorskip("mobase_tests.qt")
@@ -48,3 +47,46 @@ def test_qdatetime():
assert m.datetime_to_string(date, Qt.DateFormat.TextDate) == date.toString(
Qt.DateFormat.TextDate
)
def test_qvariant():
# Python -> C++
assert m.qvariant_from_none(None) == (True, False)
assert m.qvariant_from_int(-52) == (True, -52)
assert m.qvariant_from_int(0) == (True, 0)
assert m.qvariant_from_int(33) == (True, 33)
assert m.qvariant_from_bool(True) == (True, True)
assert m.qvariant_from_bool(False) == (True, False)
assert m.qvariant_from_str("a string") == (True, "a string")
assert m.qvariant_from_list([]) == (True, [])
assert m.qvariant_from_list([1, "hello", False]) == (True, [1, "hello", False])
assert m.qvariant_from_map({"a": 33, "b": False, "c": ["a", "b"]}) == (
True,
{"a": 33, "b": False, "c": ["a", "b"]},
)
# C++ -> Python (see .cpp file for the value)
assert m.qvariant_none() is None
assert m.qvariant_int() == 42
assert m.qvariant_bool() is True
assert m.qvariant_str() == "hello world"
assert m.qvariant_map() == {"baz": "world hello", "bar": 42, "moo": True}
assert m.qvariant_list() == [
33,
[4, "foo"],
False,
"hello",
None,
{"bar": 42, "moo": [44, True]},
45,
]
+39 -5
View File
@@ -1,8 +1,42 @@
cmake_minimum_required(VERSION 3.16)
cmake_minimum_required(VERSION 3.22)
add_executable(pythonrunner-tests)
mo2_configure_tests(pythonrunner-tests
WARNINGS OFF)
mo2_add_dependencies(pythonrunner-tests PUBLIC uibase)
# setting-up the tests for the runner is a bit complex because we need a tons of
# things
# first we configure the tests as with other tests
add_executable(pythonrunner-tests EXCLUDE_FROM_ALL)
mo2_configure_tests(pythonrunner-tests WARNINGS OFF)
# add mocks
target_include_directories(pythonrunner-tests
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../mocks)
# link to pythonrunner - we set PYTHONRUNNER_LIBRARY to not export the symbol but
# loads it directly, we are going to add the DLL path below
target_compile_definitions(pythonrunner-tests PUBLIC PYTHONRUNNER_LIBRARY)
target_link_libraries(pythonrunner-tests PUBLIC pythonrunner)
set(PYLIB_DIR ${CMAKE_CURRENT_BINARY_DIR}/pylibs)
mo2_python_pip_install(pythonrunner-tests
DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/pylibs
PACKAGES pytest PyQt6==6.3.0)
add_dependencies(pythonrunner-tests mobase)
# we set multiple properties, including:
# - updated PATH for DLLs (Qt, etc.), Python and pythonrunner
# - PYTHONPATH
set(pythoncore "${PYTHON_ROOT}/PCbuild/amd64/pythoncore")
file(GLOB pythoncorezip "${pythoncore}/python*.zip")
set(PYTHONPATH "${PYLIB_DIR}\\;$<TARGET_FILE_DIR:mobase>\\;${pythoncore}\\;${pythoncorezip}")
set(extra_paths "${MO2_INSTALL_PATH}/bin/dlls")
string(APPEND extra_paths "\\;${PYTHON_ROOT}/PCbuild/amd64")
string(APPEND extra_paths "\\;$<TARGET_FILE_DIR:pythonrunner>")
set_tests_properties(${pythonrunner-tests_gtests}
PROPERTIES
WORKING_DIRECTORY "${MO2_INSTALL_PATH}/bin"
ENVIRONMENT "PLUGIN_DIR=${CMAKE_CURRENT_SOURCE_DIR}/plugins"
ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:${extra_paths};PYTHONPATH=set:${PYTHONPATH}")
+31
View File
@@ -0,0 +1,31 @@
# -*- encoding: utf-8 -*-
import mobase
class DummyPlugin(mobase.IPlugin):
def init(self, organizer: mobase.IOrganizer) -> bool:
return True
def author(self) -> str:
return "The Author"
def name(self) -> str:
return "The Name"
def description(self) -> str:
return "The Description"
def version(self) -> mobase.VersionInfo:
return mobase.VersionInfo("1.3.0")
def settings(self) -> list[mobase.PluginSetting]:
return [
mobase.PluginSetting(
"a setting", "the setting description", default_value=12
)
]
def createPlugin() -> mobase.IPlugin:
return DummyPlugin()
+42
View File
@@ -0,0 +1,42 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "MockOrganizer.h"
#include "iplugin.h"
#include "pythonrunner.h"
#include <QCoreApplication>
using namespace MOBase;
TEST(IPlugin, Basic)
{
const auto plugins_folder = QString(std::getenv("PLUGIN_DIR"));
std::unique_ptr<IPythonRunner> runner(CreatePythonRunner());
runner->initialize();
// load objects
const auto objects = runner->load(plugins_folder + "/dummy-iplugin.py");
EXPECT_EQ(objects.size(), 1);
// load the IPlugin
const IPlugin* plugin = qobject_cast<IPlugin*>(objects[0]);
EXPECT_NE(plugin, nullptr);
EXPECT_EQ(plugin->author(), "The Author");
EXPECT_EQ(plugin->name(), "The Name");
EXPECT_EQ(plugin->version(), VersionInfo(1, 3, 0));
EXPECT_EQ(plugin->description(), "The Description");
// settings
const auto settings = plugin->settings();
EXPECT_EQ(settings.size(), 1);
EXPECT_EQ(settings[0].key, "a setting");
EXPECT_EQ(settings[0].description, "the setting description");
EXPECT_EQ(settings[0].defaultValue.userType(), QVariant::Type::Int);
EXPECT_EQ(settings[0].defaultValue.toInt(), 12);
// no translation, no custom implementation -> name()
EXPECT_EQ(plugin->localizedName(), "The Name");
}
+47
View File
@@ -0,0 +1,47 @@
#include "gtest/gtest.h"
#include "MockOrganizer.h"
#include "pythonrunner.h"
#include <QCoreApplication>
TEST(Lifetime, Plugins)
{
const auto plugins_folder = QString(std::getenv("PLUGIN_DIR"));
std::unique_ptr<IPythonRunner> runner(CreatePythonRunner());
runner->initialize();
{
const auto objects = runner->load(plugins_folder + "/dummy-iplugin.py");
// we found one plugin
EXPECT_EQ(objects.size(), 1);
// check that deleting the object actually destroys it
bool destroyed = false;
QObject::connect(objects[0], &QObject::destroyed, [&destroyed]() {
destroyed = true;
});
delete objects[0];
EXPECT_EQ(destroyed, true);
}
// same things but with a parent
{
QObject* dummy_parent = new QObject();
const auto objects = runner->load(plugins_folder + "/dummy-iplugin.py");
// we found one plugin
EXPECT_EQ(objects.size(), 1);
objects[0]->setParent(dummy_parent);
// check that deleting the object actually destroys it
bool destroyed = false;
QObject::connect(objects[0], &QObject::destroyed, [&destroyed]() {
destroyed = true;
});
delete dummy_parent;
EXPECT_EQ(destroyed, true);
}
}
-13
View File
@@ -1,13 +0,0 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "MockOrganizer.h"
using ::testing::Eq;
using ::testing::NaggyMock;
using ::testing::Return;
TEST(Organizer, Basic)
{
MockOrganizer mock;
}