mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-01-17 11:28:07 +00:00
Proto tests[ci_skip]
This commit is contained in:
parent
febdf8b4a7
commit
643f4c5942
16 changed files with 619 additions and 95 deletions
|
|
@ -322,6 +322,7 @@ else()
|
|||
message("Adding Linphone Desktop in an IDE-friendly state")
|
||||
set(CMAKE_INSTALL_PREFIX "${APPLICATION_OUTPUT_DIR}")
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/linphone-app)
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/linphone-tester)
|
||||
if(NOT LINPHONE_QT_ONLY)
|
||||
add_dependencies(app-library ${APP_DEPENDS})
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ include(CheckCXXCompilerFlag)
|
|||
set(TARGET_NAME linphone-qt)
|
||||
set(LINPHONE_QML_DIR "WORK/qml_files/ui")
|
||||
set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS true)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
|
|
@ -60,6 +60,7 @@ list(APPEND CMAKE_PREFIX_PATH "${QTKEYCHAIN_OUTPUT_DIR}/lib/cmake")
|
|||
|
||||
set(APP_LIBRARY app-library)
|
||||
set(APP_PLUGIN app-plugin)
|
||||
#set(APP_TESTER app-tester)
|
||||
include(application_info.cmake)
|
||||
string(TIMESTAMP CURRENT_YEAR "%Y")
|
||||
if(NOT APPLICATION_START_LICENCE OR "${CURRENT_YEAR}" STREQUAL "${APPLICATION_START_LICENCE}")
|
||||
|
|
@ -129,6 +130,8 @@ endif()
|
|||
if( WIN32)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_WINSOCKAPI_")#remove error from windows headers order
|
||||
add_definitions(-DNOMINMAX)
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
||||
endif()
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)#useful for config.h
|
||||
|
||||
|
|
@ -292,6 +295,8 @@ set(PLUGIN_SOURCES src/utils/plugins/PluginDataAPI.cpp
|
|||
src/utils/plugins/PluginNetworkHelper.cpp
|
||||
src/utils/plugins/LinphonePlugin.cpp
|
||||
)
|
||||
set(TESTER_SOURCES src/tester/utils_tester.cpp
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
src/app/App.hpp
|
||||
|
|
@ -432,9 +437,13 @@ set(HEADERS
|
|||
set(PLUGIN_HEADERS
|
||||
include/LinphoneApp/PluginDataAPI.hpp
|
||||
include/LinphoneApp/PluginNetworkHelper.hpp
|
||||
include/LinphoneApp/LinphonePlugin.hpp)
|
||||
include/LinphoneApp/LinphonePlugin.hpp
|
||||
)
|
||||
set(TESTER_HEADERS src/tester/utils_tester.hpp
|
||||
)
|
||||
|
||||
list(APPEND SOURCES include/LinphoneApp/PluginExample.json)
|
||||
set(MAIN_FILE src/app/main.cpp)
|
||||
#set(MAIN_FILE src/app/main.cpp)
|
||||
|
||||
if (APPLE)
|
||||
list(APPEND SOURCES
|
||||
|
|
@ -511,6 +520,8 @@ PREPEND(SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/")
|
|||
PREPEND(HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/")
|
||||
PREPEND(PLUGIN_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/")
|
||||
PREPEND(PLUGIN_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/")
|
||||
PREPEND(TESTER_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/")
|
||||
PREPEND(TESTER_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/")
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Compute QML files list.
|
||||
|
|
@ -603,7 +614,8 @@ if(WIN32)
|
|||
endif()
|
||||
|
||||
add_library(${APP_PLUGIN} SHARED ${PLUGIN_SOURCES} ${PLUGIN_HEADERS})
|
||||
add_library(${APP_LIBRARY} OBJECT ${SOURCES} ${HEADERS} ${QML_SOURCES} ${QRC_RESOURCES} ${QRC_WEBVIEW_RESOURCES} ${PLUGIN_HEADERS})#Need to add Headers to resolve moc Qt symbols
|
||||
add_library(${APP_LIBRARY} OBJECT ${SOURCES} ${HEADERS} ${QML_SOURCES} ${QRC_RESOURCES} ${QRC_WEBVIEW_RESOURCES} ${TESTER_SOURCES} ${TESTER_HEADERS} ${PLUGIN_HEADERS})#Need to add Headers to resolve moc Qt symbols
|
||||
##add_executable(${APP_TESTER} ${TESTER_SOURCES} ${TESTER_HEADERS})
|
||||
|
||||
if (WIN32)
|
||||
add_executable(${TARGET_NAME} WIN32 $<TARGET_OBJECTS:${APP_LIBRARY}> ${MAIN_FILE} ${QRC_BIG_RESOURCES} ${RC_FILE})
|
||||
|
|
@ -622,12 +634,14 @@ endif ()
|
|||
|
||||
set_property(TARGET ${APP_LIBRARY} PROPERTY POSITION_INDEPENDENT_CODE ON) #Need by Qt
|
||||
set_property(TARGET ${APP_PLUGIN} PROPERTY POSITION_INDEPENDENT_CODE ON) #Need by Qt
|
||||
#set_property(TARGET ${APP_TESTER} PROPERTY POSITION_INDEPENDENT_CODE ON) #Need by Qt
|
||||
|
||||
|
||||
#Turn on automatic resources compilation by cmake
|
||||
#Instead of excplicitely calling qt5_add_resources
|
||||
set_property(TARGET ${APP_LIBRARY} PROPERTY AUTORCC ON)
|
||||
set_property(TARGET ${APP_PLUGIN} PROPERTY AUTORCC ON)
|
||||
#set_property(TARGET ${APP_TESTER} PROPERTY AUTORCC ON)
|
||||
|
||||
|
||||
|
||||
|
|
@ -641,6 +655,10 @@ endif()
|
|||
target_compile_definitions(${APP_PLUGIN} PUBLIC "-DENABLE_APP_EXPORT_PLUGIN")
|
||||
set_target_properties(${APP_PLUGIN} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||
|
||||
#set_target_properties(${APP_TESTER} PROPERTIES WIN32_EXECUTABLE TRUE)
|
||||
#set_target_properties(${APP_TESTER} PROPERTIES MACOSX_BUNDLE TRUE)
|
||||
|
||||
|
||||
set(INCLUDED_DIRECTORIES "${LINPHONECXX_INCLUDE_DIRS}" "${MEDIASTREAMER2_INCLUDE_DIRS}")
|
||||
list(APPEND INCLUDED_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/include")
|
||||
|
||||
|
|
@ -676,6 +694,7 @@ foreach (package ${QT5_PACKAGES})
|
|||
# list(APPEND LIBRARIES ${Qt5${package}_LIBRARIES})
|
||||
target_link_libraries(${APP_LIBRARY} Qt5::${package})
|
||||
target_link_libraries(${APP_PLUGIN} Qt5::${package})
|
||||
#target_link_libraries(${APP_TESTER} Qt5::${package})
|
||||
target_link_libraries(${TARGET_NAME} Qt5::${package})
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
|
@ -696,6 +715,7 @@ endforeach ()
|
|||
if(ENABLE_QT_KEYCHAIN)
|
||||
target_link_libraries(${APP_LIBRARY} ${QTKEYCHAIN_TARGET_NAME})
|
||||
target_link_libraries(${APP_PLUGIN} ${QTKEYCHAIN_TARGET_NAME})
|
||||
#target_link_libraries(${APP_TESTER} ${QTKEYCHAIN_TARGET_NAME})
|
||||
target_link_libraries(${TARGET_NAME} ${QTKEYCHAIN_TARGET_NAME})
|
||||
endif()
|
||||
|
||||
|
|
@ -707,6 +727,7 @@ endif ()
|
|||
|
||||
target_include_directories(${APP_LIBRARY} SYSTEM PUBLIC ${INCLUDED_DIRECTORIES})
|
||||
target_include_directories(${APP_PLUGIN} SYSTEM PUBLIC ${INCLUDED_DIRECTORIES})
|
||||
#target_include_directories(${APP_TESTER} SYSTEM PUBLIC ${INCLUDED_DIRECTORIES})
|
||||
target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${INCLUDED_DIRECTORIES})
|
||||
|
||||
target_link_libraries(${APP_LIBRARY} ${LIBRARIES})
|
||||
|
|
@ -714,6 +735,7 @@ target_link_libraries(${APP_PLUGIN} ${LIBRARIES})
|
|||
target_link_libraries(${APP_LIBRARY} ${APP_PLUGIN})
|
||||
target_link_libraries(${TARGET_NAME} ${LIBRARIES})
|
||||
target_link_libraries(${TARGET_NAME} ${APP_PLUGIN})
|
||||
#target_link_libraries(${APP_TESTER} ${LIBRARIES})
|
||||
|
||||
if(WIN32)
|
||||
find_library(LDAP_LIBRARIES NAMES ldap libldap HINTS "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}")
|
||||
|
|
@ -721,8 +743,11 @@ if(WIN32)
|
|||
target_link_libraries(${TARGET_NAME} wsock32 ws2_32 ${LDAP_LIBRARIES} ${LBER_LIBRARIES})
|
||||
endif()
|
||||
|
||||
#target_link_libraries(${APP_TESTER} ${TARGET_NAME})
|
||||
|
||||
add_dependencies(${APP_LIBRARY} update_translations ${TARGET_NAME}-git-version ${APP_PLUGIN})
|
||||
add_dependencies(${TARGET_NAME} ${APP_LIBRARY} ${APP_PLUGIN})
|
||||
#add_dependencies(${APP_TESTER} ${TARGET_NAME} ${APP_LIBRARY})
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
@ -738,11 +763,6 @@ add_custom_command(TARGET ${APP_PLUGIN} POST_BUILD COMMAND ${CMAKE_COMMAND} -E c
|
|||
#add_custom_command(TARGET ${TARGET_NAME} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/include/LinphoneApp/*" "${CMAKE_INSTALL_PREFIX}/include/LinphoneApp/")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#configure_file("${CMAKE_CURRENT_SOURCE_DIR}/include/*" "${CMAKE_INSTALL_PREFIX}/include/LinphoneApp/" COPYONLY)
|
||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include" DESTINATION ".")
|
||||
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@
|
|||
|
||||
// =============================================================================
|
||||
|
||||
#include "appMain.cpp"
|
||||
using namespace std;
|
||||
|
||||
namespace {
|
||||
|
|
@ -347,6 +348,8 @@ void App::initContentApp () {
|
|||
configPath = getConfigPathIfExists(*mParser);
|
||||
config = Utils::getConfigIfExists (QString::fromStdString(configPath));
|
||||
initLocale(config);
|
||||
// Init engine content.
|
||||
mEngine = new QQmlApplicationEngine(this);
|
||||
} else {
|
||||
configPath = getConfigPathIfExists(*mParser);
|
||||
config = Utils::getConfigIfExists(QString::fromStdString(configPath));
|
||||
|
|
@ -367,6 +370,8 @@ void App::initContentApp () {
|
|||
#ifndef Q_OS_MACOS
|
||||
mustBeIconified = mParser->isSet("iconified");
|
||||
#endif // ifndef Q_OS_MACOS
|
||||
// Init engine content.
|
||||
mEngine = new QQmlApplicationEngine(this);
|
||||
mColorListModel = new ColorListModel();
|
||||
mImageListModel = new ImageListModel();
|
||||
}
|
||||
|
|
@ -382,8 +387,7 @@ void App::initContentApp () {
|
|||
CoreManager::init(this, Utils::coreStringToAppString(configPath));
|
||||
|
||||
|
||||
// Init engine content.
|
||||
mEngine = new QQmlApplicationEngine(this);
|
||||
|
||||
|
||||
// Provide `+custom` folders for custom components and `5.9` for old components.
|
||||
{
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ namespace linphone {
|
|||
class Config;
|
||||
}
|
||||
|
||||
class AppController;
|
||||
class ColorListModel;
|
||||
class DefaultTranslator;
|
||||
class ImageListModel;
|
||||
|
|
@ -53,6 +54,7 @@ class App : public SingleApplication {
|
|||
Q_PROPERTY(bool autoStart READ getAutoStart WRITE setAutoStart NOTIFY autoStartChanged)
|
||||
|
||||
public:
|
||||
static int main(int argc, char *argv[]);
|
||||
App (int &argc, char *argv[]);
|
||||
~App ();
|
||||
|
||||
|
|
|
|||
102
linphone-app/src/app/appMain.cpp
Normal file
102
linphone-app/src/app/appMain.cpp
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "AppController.hpp"
|
||||
#include <qloggingcategory.h>
|
||||
#ifdef QT_QML_DEBUG
|
||||
#include <QQmlDebuggingEnabler>
|
||||
#endif
|
||||
#include <QSurfaceFormat>
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
FILE * gStream = NULL;
|
||||
#endif
|
||||
|
||||
#include "components/core/CoreManager.hpp"
|
||||
#include "utils/Utils.hpp"
|
||||
|
||||
#ifdef ENABLE_QT_KEYCHAIN
|
||||
#include "components/vfs/VfsUtils.hpp"
|
||||
#endif
|
||||
|
||||
// =============================================================================
|
||||
|
||||
void cleanStream(){
|
||||
#ifdef _WIN32
|
||||
if(gStream) {
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
fclose(gStream);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int App::main (int argc, char *argv[]) {
|
||||
#ifdef __APPLE__
|
||||
qputenv("QT_ENABLE_GLYPH_CACHE_WORKAROUND", "1"); // On Mac, set this workaround to avoid glitches on M1, because of https://bugreports.qt.io/browse/QTBUG-89379
|
||||
#elif defined _WIN32
|
||||
// log in console only if launched from console
|
||||
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||
freopen_s(&gStream, "CONOUT$", "w", stdout);
|
||||
freopen_s(&gStream, "CONOUT$", "w", stderr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_QT_KEYCHAIN
|
||||
bool vfsEncrypted = VfsUtils::updateSDKWithKey();
|
||||
#else
|
||||
bool vfsEncrypted = false;
|
||||
#endif
|
||||
|
||||
AppController controller(argc, argv);
|
||||
#ifdef QT_QML_DEBUG
|
||||
QQmlDebuggingEnabler enabler;
|
||||
#endif
|
||||
//QLoggingCategory::setFilterRules("*.debug=true;qml=false");
|
||||
App *app = controller.getApp();
|
||||
// if(vfsEncrypted)
|
||||
// qInfo() << "Activation of VFS encryption.";
|
||||
if (app->isSecondary())
|
||||
{
|
||||
qInfo() << QStringLiteral("Running secondary app success. Kill it now.");
|
||||
cleanStream();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
qInfo() << QStringLiteral("Running app...");
|
||||
|
||||
int ret;
|
||||
do {
|
||||
app->initContentApp();
|
||||
ret = app->exec();
|
||||
} while (ret == App::RestartCode);
|
||||
qWarning() << "Exiting app with the code : " << ret;
|
||||
controller.stopApp(); // Stopping app before core to let time to GUI to process needed items from linphone.
|
||||
if( CoreManager::getInstance()){
|
||||
auto core = CoreManager::getInstance()->getCore();
|
||||
if(core && core->getGlobalState() == linphone::GlobalState::On)
|
||||
core->stop();
|
||||
}
|
||||
cleanStream();
|
||||
if( ret == App::DeleteDataCode){
|
||||
Utils::deleteAllUserDataOffline();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -17,88 +17,9 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "AppController.hpp"
|
||||
#include <qloggingcategory.h>
|
||||
#ifdef QT_QML_DEBUG
|
||||
#include <QQmlDebuggingEnabler>
|
||||
#endif
|
||||
#include <QSurfaceFormat>
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
FILE * gStream = NULL;
|
||||
#endif
|
||||
|
||||
#include "components/core/CoreManager.hpp"
|
||||
#include "utils/Utils.hpp"
|
||||
|
||||
#ifdef ENABLE_QT_KEYCHAIN
|
||||
#include "components/vfs/VfsUtils.hpp"
|
||||
#endif
|
||||
|
||||
// =============================================================================
|
||||
|
||||
void cleanStream(){
|
||||
#ifdef _WIN32
|
||||
if(gStream) {
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
fclose(gStream);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#include "App.hpp"
|
||||
|
||||
|
||||
int main (int argc, char *argv[]) {
|
||||
#ifdef __APPLE__
|
||||
qputenv("QT_ENABLE_GLYPH_CACHE_WORKAROUND", "1"); // On Mac, set this workaround to avoid glitches on M1, because of https://bugreports.qt.io/browse/QTBUG-89379
|
||||
#elif defined _WIN32
|
||||
// log in console only if launched from console
|
||||
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||
freopen_s(&gStream, "CONOUT$", "w", stdout);
|
||||
freopen_s(&gStream, "CONOUT$", "w", stderr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_QT_KEYCHAIN
|
||||
bool vfsEncrypted = VfsUtils::updateSDKWithKey();
|
||||
#else
|
||||
bool vfsEncrypted = false;
|
||||
#endif
|
||||
|
||||
AppController controller(argc, argv);
|
||||
#ifdef QT_QML_DEBUG
|
||||
QQmlDebuggingEnabler enabler;
|
||||
#endif
|
||||
//QLoggingCategory::setFilterRules("*.debug=true;qml=false");
|
||||
App *app = controller.getApp();
|
||||
if(vfsEncrypted)
|
||||
qInfo() << "Activation of VFS encryption.";
|
||||
if (app->isSecondary())
|
||||
{
|
||||
qInfo() << QStringLiteral("Running secondary app success. Kill it now.");
|
||||
cleanStream();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
qInfo() << QStringLiteral("Running app...");
|
||||
|
||||
int ret;
|
||||
do {
|
||||
app->initContentApp();
|
||||
ret = app->exec();
|
||||
} while (ret == App::RestartCode);
|
||||
qWarning() << "Exiting app with the code : " << ret;
|
||||
controller.stopApp(); // Stopping app before core to let time to GUI to process needed items from linphone.
|
||||
if( CoreManager::getInstance()){
|
||||
auto core = CoreManager::getInstance()->getCore();
|
||||
if(core && core->getGlobalState() == linphone::GlobalState::On)
|
||||
core->stop();
|
||||
}
|
||||
cleanStream();
|
||||
if( ret == App::DeleteDataCode){
|
||||
Utils::deleteAllUserDataOffline();
|
||||
}
|
||||
return ret;
|
||||
return App::main(argc, argv);
|
||||
}
|
||||
|
|
|
|||
343
linphone-app/src/tester/utils_tester.cpp
Normal file
343
linphone-app/src/tester/utils_tester.cpp
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "utils_tester.hpp"
|
||||
|
||||
#include "app/App.hpp"
|
||||
#include "app/AppController.hpp"
|
||||
#include "components/core/CoreManager.hpp"
|
||||
#include "utils/Utils.hpp"
|
||||
|
||||
#include <QThread>
|
||||
|
||||
/*
|
||||
#include <QtTest/qtest_gui.h>
|
||||
|
||||
#define L_QTEST_MAIN_IMPL(TestObject) \
|
||||
TESTLIB_SELFCOVERAGE_START(#TestObject) \
|
||||
QT_PREPEND_NAMESPACE(QTest::Internal::callInitMain)<TestObject>(); \
|
||||
AppController app(argc, argv); \
|
||||
TestObject tc(&app); \
|
||||
QTEST_SET_MAIN_SOURCE_PATH \
|
||||
return QTest::qExec(&tc, argc, argv);
|
||||
|
||||
#define L_QTEST_MAIN(TestObject) \
|
||||
int main(int argc, char *argv[]) \
|
||||
{ \
|
||||
L_QTEST_MAIN_IMPL(TestObject) \
|
||||
}
|
||||
L_QTEST_MAIN(UtilsTester)
|
||||
|
||||
//QTEST_MAIN(UtilsTester)
|
||||
//#include "utilstester.moc" // if hpp is in cpp
|
||||
*/
|
||||
|
||||
|
||||
#include <qloggingcategory.h>
|
||||
#ifdef QT_QML_DEBUG
|
||||
#include <QQmlDebuggingEnabler>
|
||||
#endif
|
||||
#include <QSurfaceFormat>
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
FILE * gStream = NULL;
|
||||
#endif
|
||||
|
||||
#include "components/core/CoreManager.hpp"
|
||||
#include "utils/Utils.hpp"
|
||||
|
||||
#ifdef ENABLE_QT_KEYCHAIN
|
||||
#include "components/vfs/VfsUtils.hpp"
|
||||
#endif
|
||||
#include <QQuickItem>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQuickWindow>
|
||||
#include <QQmlProperty>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
void _cleanStream(){
|
||||
#ifdef _WIN32
|
||||
if(gStream) {
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
fclose(gStream);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
class Test : public QThread{
|
||||
public:
|
||||
Test(){
|
||||
}
|
||||
virtual void run();
|
||||
};
|
||||
void Test::run(){
|
||||
UtilsTester tc(nullptr);
|
||||
std::vector<std::string> arguments = {"dummy", "-maxwarnings", "0"};
|
||||
std::vector<char*> argv;
|
||||
for (const auto& arg : arguments)
|
||||
argv.push_back((char*)arg.data());
|
||||
argv.push_back(nullptr);
|
||||
|
||||
QTest::qExec(&tc, argv.size() - 1, argv.data());
|
||||
}
|
||||
int main (int argc, char *argv[]) {
|
||||
#ifdef __APPLE__
|
||||
qputenv("QT_ENABLE_GLYPH_CACHE_WORKAROUND", "1"); // On Mac, set this workaround to avoid glitches on M1, because of https://bugreports.qt.io/browse/QTBUG-89379
|
||||
#elif defined _WIN32
|
||||
// log in console only if launched from console
|
||||
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||
freopen_s(&gStream, "CONOUT$", "w", stdout);
|
||||
freopen_s(&gStream, "CONOUT$", "w", stderr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_QT_KEYCHAIN
|
||||
bool vfsEncrypted = VfsUtils::updateSDKWithKey();
|
||||
#else
|
||||
bool vfsEncrypted = false;
|
||||
#endif
|
||||
|
||||
AppController controller(argc, argv);
|
||||
#ifdef QT_QML_DEBUG
|
||||
QQmlDebuggingEnabler enabler;
|
||||
#endif
|
||||
//QLoggingCategory::setFilterRules("*.debug=true;qml=false");
|
||||
App *app = controller.getApp();
|
||||
// if(vfsEncrypted)
|
||||
// qInfo() << "Activation of VFS encryption.";
|
||||
if (app->isSecondary())
|
||||
{
|
||||
qInfo() << QStringLiteral("Running secondary app success. Kill it now.");
|
||||
_cleanStream();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
qInfo() << QStringLiteral("Running app...");
|
||||
QThread * test;
|
||||
int ret = 0;
|
||||
do {
|
||||
app->initContentApp();
|
||||
if(ret == 0){
|
||||
test = QThread::create([](){
|
||||
UtilsTester tc(nullptr);
|
||||
std::vector<std::string> arguments = {"dummy", "-maxwarnings", "0"};
|
||||
std::vector<char*> argv;
|
||||
for (const auto& arg : arguments)
|
||||
argv.push_back((char*)arg.data());
|
||||
argv.push_back(nullptr);
|
||||
|
||||
QTest::qExec(&tc, argv.size() - 1, argv.data());
|
||||
});
|
||||
test->start();
|
||||
}
|
||||
ret = app->exec();
|
||||
} while (ret == App::RestartCode);
|
||||
qWarning() << "Exiting app with the code : " << ret;
|
||||
controller.stopApp(); // Stopping app before core to let time to GUI to process needed items from linphone.
|
||||
if( CoreManager::getInstance()){
|
||||
auto core = CoreManager::getInstance()->getCore();
|
||||
if(core && core->getGlobalState() == linphone::GlobalState::On)
|
||||
core->stop();
|
||||
}
|
||||
_cleanStream();
|
||||
if( ret == App::DeleteDataCode){
|
||||
Utils::deleteAllUserDataOffline();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void debugNames(QQuickItem * item){
|
||||
if(!item)
|
||||
return;
|
||||
auto childs = item->childItems();
|
||||
QString name = item->objectName();
|
||||
QString qmlProperty = QQmlProperty::read(item, "objectName").toString();
|
||||
const QMetaObject* metaObject = item->metaObject();
|
||||
QStringList properties;
|
||||
for(int i = metaObject->propertyOffset(); i < metaObject->propertyCount(); ++i)
|
||||
properties << QString::fromLatin1(metaObject->property(i).name());
|
||||
|
||||
if(name != "" || qmlProperty != "")
|
||||
qDebug() << name << " / " << qmlProperty << " / " << childs.size() << properties;
|
||||
else{
|
||||
if(metaObject->propertyCount() > 0)
|
||||
qDebug() << properties;
|
||||
}
|
||||
for(int i = 0 ; i < childs.size() ; ++i){
|
||||
debugNames(childs.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
class ItemManager{
|
||||
public:
|
||||
static void getItem(const QString& name, QQuickItem * item, QQuickItem ** found){
|
||||
if(*found || !item)
|
||||
return;
|
||||
auto childs = item->childItems();
|
||||
//QString itemName = item->objectName(); // not working on Repeater
|
||||
if(QQmlProperty::read(item, "objectName").toString() == name){
|
||||
//if(name == itemName){
|
||||
*found = item;
|
||||
return;
|
||||
}
|
||||
for(int i = 0 ; i < childs.size() ; ++i){
|
||||
ItemManager::getItem(name, childs.at(i), found);
|
||||
}
|
||||
}
|
||||
static QQuickItem * getItem(QQuickItem * parent, const QString& name){
|
||||
QQuickItem * item = nullptr;
|
||||
if(parent){
|
||||
ItemManager::getItem(name, parent, &item);
|
||||
if(!item){
|
||||
qWarning() << "not found : " << name;
|
||||
debugNames(parent);
|
||||
//debugNames(engine);
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
static QQuickItem * getItem(const QString& name){
|
||||
auto engine = App::getInstance()->getEngine();
|
||||
QQuickItem * item = nullptr;
|
||||
auto roots = engine->rootObjects();
|
||||
for(int i = 0 ; !item && i < roots.size() ; ++i){
|
||||
QObject * object = roots[i];
|
||||
auto childs = object->children();
|
||||
for(int j = 0 ; !item && j < childs.size() ; ++j)
|
||||
ItemManager::getItem(name, qobject_cast<QQuickItem*>(childs[j]), &item);
|
||||
}
|
||||
|
||||
if(!item){
|
||||
auto childs = App::getInstance()->getCallsWindow()->children();
|
||||
for(int j = 0 ; !item && j < childs.size() ; ++j)
|
||||
ItemManager::getItem(name, qobject_cast<QQuickItem*>(childs[j]), &item);
|
||||
//ItemManager::getItem(qobject_cast<QQuickItem*>(App::getInstance()->getCallsWindow()), name);
|
||||
}
|
||||
if(!item) {
|
||||
//qWarning() << "not found : " << name << " / " << roots.size();
|
||||
//debugNames(qobject_cast<QQuickItem*>(App::getInstance()->getCallsWindow()));
|
||||
/*
|
||||
auto roots = engine->rootObjects();
|
||||
for(int i = 0 ; !item && i < roots.size() ; ++i){
|
||||
QObject * object = roots[i];
|
||||
auto childs = object->children();
|
||||
for(int j = 0 ; j < childs.size() ; ++j)
|
||||
debugNames(qobject_cast<QQuickItem*>(childs[j]));
|
||||
}*/
|
||||
}
|
||||
return item;
|
||||
//return qobject_cast<QQuickItem*>(item);
|
||||
}
|
||||
|
||||
|
||||
static void clickItem( QQuickItem* pItem, QQuickWindow* pRootWindow ){
|
||||
QVERIFY(pItem != nullptr);
|
||||
auto oPointF = pItem->mapToScene( QPoint( 0, 0 ) );
|
||||
auto oPoint = oPointF.toPoint();
|
||||
oPoint.rx() += pItem->width() / 2;
|
||||
oPoint.ry() += pItem->height() / 2;
|
||||
QTest::mouseClick( pRootWindow, Qt::LeftButton, Qt::NoModifier, oPoint );
|
||||
}
|
||||
|
||||
static void clickItem(const QString& name){
|
||||
QQuickItem * item = nullptr;
|
||||
QTRY_VERIFY_WITH_TIMEOUT( (item = ItemManager::getItem(name)) != nullptr, 5000);
|
||||
clickItem(item, App::getInstance()->getMainWindow());
|
||||
//wrapperItem = view->findChild<QQuickItem *>("__internalWrapper");
|
||||
}
|
||||
|
||||
static void clickItem(const QString& parentName, const QString& name){
|
||||
QQuickItem * parentItem = nullptr;
|
||||
QQuickItem * item = nullptr;
|
||||
QTRY_VERIFY_WITH_TIMEOUT( (parentItem =ItemManager::getItem(parentName)) != nullptr, 5000);
|
||||
QTRY_VERIFY_WITH_TIMEOUT( (item = ItemManager::getItem(parentItem, name)) != nullptr, 5000);
|
||||
clickItem(item, App::getInstance()->getMainWindow());
|
||||
//wrapperItem = view->findChild<QQuickItem *>("__internalWrapper");
|
||||
}
|
||||
|
||||
static void keyItem(const QString& sequence){
|
||||
for(int i = 0 ; i < sequence.size() ; ++i)
|
||||
QTest::keyClick(App::getInstance()->getMainWindow(), sequence[i].toLatin1() );
|
||||
}
|
||||
};
|
||||
|
||||
UtilsTester::UtilsTester(AppController * controller){
|
||||
mController = controller;
|
||||
QTRY_VERIFY_WITH_TIMEOUT(CoreManager::getInstance() != nullptr, 5000);
|
||||
QTRY_VERIFY_WITH_TIMEOUT(CoreManager::getInstance()->isInitialized(), 5000);
|
||||
}
|
||||
|
||||
UtilsTester::~UtilsTester(){
|
||||
if(App::getInstance())
|
||||
App::getInstance()->exit(0);
|
||||
delete mController;
|
||||
}
|
||||
|
||||
void UtilsTester::initTestCase(){
|
||||
QTRY_VERIFY_WITH_TIMEOUT(CoreManager::getInstance() != nullptr, 5000);
|
||||
QTRY_VERIFY_WITH_TIMEOUT(CoreManager::getInstance()->isInitialized(), 5000);
|
||||
//App::main(1, (char**)&appPtr);
|
||||
}
|
||||
|
||||
void UtilsTester::cleanupTestCase(){
|
||||
//App::getInstance()->exit(App::RestartCode);
|
||||
App::getInstance()->exit(0);
|
||||
}
|
||||
|
||||
void UtilsTester::test_osProduct() {
|
||||
QString product = Utils::getOsProduct();
|
||||
QVERIFY(!product.contains(' '));
|
||||
}
|
||||
|
||||
void UtilsTester::test_register(){
|
||||
QTRY_VERIFY_WITH_TIMEOUT(CoreManager::getInstance()->getCore()->getDefaultAccount()->getState() == linphone::RegistrationState::Ok, 10000);
|
||||
}
|
||||
|
||||
void UtilsTester::test_call(){
|
||||
for(int i = 0 ; i < 100 ; ++i){
|
||||
ItemManager::clickItem("__mainSmartSearchBar");
|
||||
ItemManager::keyItem("julienw2@sip.linphone.org");
|
||||
ItemManager::clickItem("__ActionBar", "__audioCallButton");
|
||||
QQuickItem *item;
|
||||
QTRY_VERIFY_WITH_TIMEOUT( (item = ItemManager::getItem("__hangupCallButton")) != nullptr, 5000);
|
||||
QTest::qWait(2000);
|
||||
ItemManager::clickItem(item, App::getInstance()->getCallsWindow());
|
||||
QTest::qWait(2000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void UtilsTester::test_call2(){
|
||||
//for(int i = 0 ; i < 50 ; ++i){
|
||||
qWarning() << "A";
|
||||
ItemManager::clickItem("__mainSmartSearchBar");
|
||||
qWarning() << "B";
|
||||
ItemManager::keyItem("julienw2@sip.linphone.org");
|
||||
qWarning() << "C";
|
||||
ItemManager::clickItem("__ActionBar", "__audioCallButton");
|
||||
QQuickItem *item;
|
||||
QTRY_VERIFY_WITH_TIMEOUT( (item = ItemManager::getItem("__hangupCallButton")) != nullptr, 5000);
|
||||
QTest::qWait(1000);// Let 1 second and end of call
|
||||
ItemManager::clickItem("__hangupCallButton");
|
||||
QTest::qWait(2000);// Let 1 second and end of call
|
||||
//}
|
||||
}
|
||||
44
linphone-app/src/tester/utils_tester.hpp
Normal file
44
linphone-app/src/tester/utils_tester.hpp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-desktop
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QTest>
|
||||
|
||||
class AppController;
|
||||
|
||||
class UtilsTester: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
std::string app;
|
||||
char * appPtr;
|
||||
int argc = 1;
|
||||
QThread * mApplication;
|
||||
AppController * mController;
|
||||
public:
|
||||
UtilsTester(AppController * controller);
|
||||
~UtilsTester();
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
void test_osProduct();
|
||||
void test_register();
|
||||
void test_call();
|
||||
void test_call2();
|
||||
};
|
||||
|
|
@ -11,7 +11,7 @@ import Common 1.0
|
|||
|
||||
Item {
|
||||
id: wrappedButton
|
||||
objectName: '__internalActionButton'
|
||||
//objectName: '__internalActionButton'
|
||||
|
||||
property color defaultBackgroundColor: 'white'
|
||||
property color defaultForegroundColor: 'black'
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import Linphone.Styles 1.0
|
|||
|
||||
SearchBox {
|
||||
id: searchBox
|
||||
objectName: '__SmartSearchBar'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
|
@ -61,8 +62,10 @@ SearchBox {
|
|||
|
||||
SipAddressesView {
|
||||
id: view
|
||||
objectName: '__SipAddressesView'
|
||||
|
||||
actions: [{
|
||||
objectName: '__videoCallButton',
|
||||
colorSet: SipAddressesViewStyle.videoCall,
|
||||
secure: 0,
|
||||
visible: true,
|
||||
|
|
@ -72,6 +75,7 @@ SearchBox {
|
|||
},
|
||||
visible: SettingsModel.videoSupported && SettingsModel.outgoingCallsEnabled && SettingsModel.showStartVideoCallButton
|
||||
}, {
|
||||
objectName: '__audioCallButton',
|
||||
colorSet: SipAddressesViewStyle.call,
|
||||
secure: 0,
|
||||
visible: true,
|
||||
|
|
@ -81,6 +85,7 @@ SearchBox {
|
|||
},
|
||||
visible: SettingsModel.outgoingCallsEnabled
|
||||
}, {
|
||||
objectName: '__chatButton',
|
||||
colorSet: SettingsModel.getShowStartChatButton() ? SipAddressesViewStyle.chat : SipAddressesViewStyle.history,
|
||||
secure: 0,
|
||||
handler: function (entry) {
|
||||
|
|
@ -90,6 +95,7 @@ SearchBox {
|
|||
visible: SettingsModel.standardChatEnabled,
|
||||
zz: 'toto'
|
||||
}, {
|
||||
objectName: '__secureChatButton',
|
||||
colorSet: SettingsModel.getShowStartChatButton() ? SipAddressesViewStyle.chat : SipAddressesViewStyle.history,
|
||||
secure: 1,
|
||||
visible: SettingsModel.secureChatEnabled && AccountSettingsModel.conferenceUri != '',
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ ScrollableListView {
|
|||
// ---------------------------------------------------------------------------
|
||||
|
||||
header: MouseArea {
|
||||
objectName: '__MouseArea'
|
||||
height: {
|
||||
var height = headerButton.visible ? SipAddressesViewStyle.header.button.height : 0
|
||||
if (defaultContact.visible) {
|
||||
|
|
@ -78,6 +79,7 @@ ScrollableListView {
|
|||
|
||||
Loader {
|
||||
id: defaultContact
|
||||
objectName: '__DefaultContact'
|
||||
|
||||
height: SipAddressesViewStyle.entry.height
|
||||
width: parent.width
|
||||
|
|
@ -85,6 +87,7 @@ ScrollableListView {
|
|||
visible: sipAddressesView.interpretableSipAddress.length > 0
|
||||
|
||||
sourceComponent: Rectangle {
|
||||
objectName: '__Rectangle'
|
||||
anchors.fill: parent
|
||||
color: SipAddressesViewStyle.entry.color.normal.color
|
||||
|
||||
|
|
@ -111,13 +114,16 @@ ScrollableListView {
|
|||
|
||||
ActionBar {
|
||||
id: defaultContactActionBar
|
||||
objectName: '__ActionBar'
|
||||
|
||||
iconSize: SipAddressesViewStyle.entry.iconSize
|
||||
|
||||
Repeater {
|
||||
model: sipAddressesView.actions
|
||||
|
||||
objectName: '__Repeater'
|
||||
delegate:Component{
|
||||
ActionButton {
|
||||
objectName: (sipAddressesView.actions[index].objectName ? sipAddressesView.actions[index].objectName : '')
|
||||
isCustom: true
|
||||
backgroundRadius: 90
|
||||
colorSet: modelData.colorSet
|
||||
|
|
@ -139,6 +145,7 @@ ScrollableListView {
|
|||
anchors.horizontalCenter: parent.right
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import 'CallsWindow.js' as Logic
|
|||
|
||||
Window {
|
||||
id: window
|
||||
objectName: '__CallWindow'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import 'qrc:/ui/scripts/Utils/utils.js' as Utils
|
|||
|
||||
Rectangle {
|
||||
id: mainItem
|
||||
objectName: '__Incall'
|
||||
|
||||
property CallModel callModel
|
||||
property ConferenceModel conferenceModel: callModel && callModel.conferenceModel
|
||||
|
|
@ -571,6 +572,7 @@ Rectangle {
|
|||
onClicked: callModel.pausedByUser = !callModel.pausedByUser
|
||||
}
|
||||
ActionButton{
|
||||
objectName: '__hangupCallButton'
|
||||
isCustom: true
|
||||
backgroundRadius: width/2
|
||||
colorSet: IncallStyle.buttons.hangup
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import 'qrc:/ui/scripts/Utils/utils.js' as Utils
|
|||
|
||||
ApplicationWindow {
|
||||
id: window
|
||||
objectName: "__MainWindow"
|
||||
|
||||
property string _currentView
|
||||
property var _lockedInfo
|
||||
|
|
@ -176,6 +177,7 @@ ApplicationWindow {
|
|||
|
||||
SmartSearchBar {
|
||||
id: smartSearchBar
|
||||
objectName: '__mainSmartSearchBar'
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
|
|
|
|||
21
linphone-tester/CMakeLists.txt
Normal file
21
linphone-tester/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(UtilsTester LANGUAGES CXX)
|
||||
|
||||
enable_testing()
|
||||
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Gui Test)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui Test)
|
||||
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
add_executable(UtilsTester tst_utilstester.cpp)
|
||||
add_test(NAME UtilsTester COMMAND UtilsTester)
|
||||
|
||||
target_link_libraries(UtilsTester PRIVATE Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Test)
|
||||
|
||||
48
linphone-tester/tst_utilstester.cpp
Normal file
48
linphone-tester/tst_utilstester.cpp
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#include <QtTest>
|
||||
#include <QCoreApplication>
|
||||
|
||||
// add necessary includes here
|
||||
|
||||
class UtilsTester : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
UtilsTester();
|
||||
~UtilsTester();
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
void test_case1();
|
||||
|
||||
};
|
||||
|
||||
UtilsTester::UtilsTester()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
UtilsTester::~UtilsTester()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UtilsTester::initTestCase()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UtilsTester::cleanupTestCase()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UtilsTester::test_case1()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QTEST_MAIN(UtilsTester)
|
||||
|
||||
#include "tst_utilstester.moc"
|
||||
Loading…
Add table
Reference in a new issue