tests(general): refactor tests

Enhance the CMake strucuture of thests
Update documentation
This commit is contained in:
Stefan 2022-04-19 21:45:26 +04:00 committed by Michał Cieślak
parent 59fc0a6669
commit e293f80554
16 changed files with 122 additions and 72 deletions

View File

@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.19)
project(StatusQ)
add_subdirectory(tests)

View File

@ -1,11 +1,11 @@
cmake_minimum_required(VERSION 3.5)
project(TestControls LANGUAGES CXX)
project(TestStatusQ LANGUAGES CXX)
enable_testing()
# TODO: Workaround until we make StatusQ a CMake library
list(APPEND QML_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/../../src/")
list(APPEND QML_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/../src/")
set(QML_IMPORT_PATH "${QML_DIRS}" CACHE STRING "Qt Creator extra qml import paths")
set(QML2_IMPORT_PATH "${QML_DIRS}" CACHE STRING "Qt Creator extra qml import paths")
@ -14,18 +14,20 @@ find_package(Qt${QT_VERSION_MAJOR} COMPONENTS QuickTest Qml Quick REQUIRED)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# no need to copy around qml test files for shadow builds - just set the respective define
add_definitions(-DQUICK_TEST_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
add_executable(${PROJECT_NAME} main.cpp)
add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME})
add_test(NAME ${PROJECT_NAME} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME} -input "${CMAKE_CURRENT_SOURCE_DIR}")
add_custom_target("Run_${PROJECT_NAME}" COMMAND ${CMAKE_CTEST_COMMAND} --test-dir "${CMAKE_CURRENT_BINARY_DIR}")
add_dependencies("Run_${PROJECT_NAME}" ${PROJECT_NAME})
# TODO: move this to a test helpers library
target_include_directories(${PROJECT_NAME}
@ -33,11 +35,10 @@ target_include_directories(${PROJECT_NAME}
${CMAKE_CURRENT_SOURCE_DIR}
)
add_subdirectory(TestHelpers)
add_subdirectory(src)
target_link_libraries(${PROJECT_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::QuickTest
Qt${QT_VERSION_MAJOR}::Qml
Qt${QT_VERSION_MAJOR}::Quick
)

View File

@ -1,14 +0,0 @@
pragma Singleton
import QtQml 2.14
import QtTest 1.0
QtObject {
//> Simulate key and wait for side effects
function pressKeyAndWait(test, item, key) {
test.keyClick(key)
test.waitForRendering(item)
}
}

View File

@ -1,32 +0,0 @@
#pragma once
#include <QQuickItem>
#include <QtGlobal>
#include <memory>
#include <mutex>
///
/// \brief Monitor output for tests and declarativelly control message handler availability
/// \todo Check that QML doesn't keep instance between test runs
///
class MonitorQtOutput : public QQuickItem
{
Q_OBJECT
public:
MonitorQtOutput();
~MonitorQtOutput();
Q_INVOKABLE QString qtOuput();
signals:
private:
static void qtMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg);
// Use it to keep track of qInstallMessageHandler call
static std::weak_ptr<QString> m_qtMessageOutputForSharing;
static std::mutex m_mutex;
std::shared_ptr<QString> m_thisMessageOutput;
int m_start = 0;
};

View File

@ -26,7 +26,8 @@ Item {
//
// Test guards
function initTestCase() {
function init() {
qtOuput.restartCapturing()
}
function cleanup() {

View File

@ -38,7 +38,8 @@ Item {
//
// Test guards
function initTestCase() {
function init() {
qtOuput.restartCapturing()
mouseClick(statusInput)
}

View File

@ -0,0 +1 @@
---

View File

@ -0,0 +1,3 @@
Start testing: Apr 18 18:49 CEST
----------------------------------------------------------
End testing: Apr 18 18:49 CEST

View File

@ -14,13 +14,11 @@ public slots:
void qmlEngineAvailable(QQmlEngine *engine)
{
// TODO: Workaround until we make StatusQ a CMake library
engine->addImportPath("../../src/");
engine->addImportPath(".");
engine->addImportPath("../src/");
engine->addImportPath("./qml/");
// TODO: Alternative to not yet supported QML_ELEMENT
qmlRegisterType<MonitorQtOutput>("StatusQ.TestHelpers", 0, 1, "MonitorQtOutput");
}
private:
MonitorQtOutput _monitorOutput;
};
QUICK_TEST_MAIN_WITH_SETUP(TestControls, TestSetup)

View File

@ -0,0 +1,23 @@
pragma Singleton
import QtQml 2.14
import QtTest 1.0
QtObject {
//> Simulate key and wait for side effects
function pressKeyAndWait(test, item, key) {
test.keyClick(key)
ensureRendered(test, item)
}
function ensureRendered(test, item) {
test.verify(test.waitForRendering(item, 1000))
}
function expectRendering(test, item) {
test.verify(test.isPolishScheduled(item))
test.verify(test.waitForRendering(item, 1000))
}
}

View File

@ -5,18 +5,21 @@
CMake
```sh
cd StatusQ/tests/TestControls
cd ./tests/
cmake -B ./build/ -S .
cmake --build ./build/
./build/TestControls
ctest --test-dir ./build/
```
QtCreator
- Open the StatusQ/tests/CMakeLists.txt
- Open the `./tests/CMakeLists.txt`
- Choose a QT kit to run the tests
- In the `Test Results` panel choose Run All Tests
- Set `%{sourceDir}/tests` as Working Directory for the TestStatusQ target
- In the *Test Results* panel choose Run All Tests or just run the *TestStatusQ* target
## TODO
- [ ] Consolidate and integrate with https://github.com/status-im/desktop-ui-tests
- [ ] TestHelpers library
- [ ] Consolidate and integrate with https://github.com/status-im/desktop-ui-tests
- [ ] Separate projects per scope: TestControls, TestComponents

View File

@ -0,0 +1,6 @@
target_include_directories(${PROJECT_NAME}
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
add_subdirectory(TestHelpers)

View File

@ -1,8 +1,3 @@
target_include_directories(${PROJECT_NAME}
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
target_sources(${PROJECT_NAME}
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/MonitorQtOutput.h

View File

@ -5,19 +5,21 @@
std::weak_ptr<QString> MonitorQtOutput::m_qtMessageOutputForSharing;
std::mutex MonitorQtOutput::m_mutex;
QtMessageHandler MonitorQtOutput::m_previousHandler = nullptr;
MonitorQtOutput::MonitorQtOutput()
{
// Ensure only one instance registers a handler
// Warning: don't QT's call loger functions inside the critical section
// Warning: don't call QT's logger functions inside the critical section
std::unique_lock<std::mutex> localLock(m_mutex);
auto globalMsgOut = m_qtMessageOutputForSharing.lock();
auto prev = qInstallMessageHandler(qtMessageOutput);
if(prev != qtMessageOutput)
m_previousHandler = prev;
if(!globalMsgOut) {
// Install message handler if not already done
m_thisMessageOutput = std::make_shared<QString>();
m_qtMessageOutputForSharing = m_thisMessageOutput;
qInstallMessageHandler(qtMessageOutput);
}
else {
m_thisMessageOutput = globalMsgOut;
@ -42,11 +44,25 @@ MonitorQtOutput::qtMessageOutput(QtMsgType type, const QMessageLogContext &conte
auto globalMsgOut = m_qtMessageOutputForSharing.lock();
assert(globalMsgOut != nullptr);
globalMsgOut->append(msg + '\n');
// Also reproduce the default output
m_previousHandler(type, context, msg);
}
QString
MonitorQtOutput::qtOuput()
{
std::unique_lock<std::mutex> localLock(m_mutex);
assert(m_thisMessageOutput->length() >= m_start);
return m_thisMessageOutput->right(m_thisMessageOutput->length() - m_start);
}
void
MonitorQtOutput::restartCapturing()
{
std::unique_lock<std::mutex> localLock(m_mutex);
// Ensure the messageHandler is installed. Foun to be reset at test initializaiton
auto prev = qInstallMessageHandler(qtMessageOutput);
if(prev != qtMessageOutput)
m_previousHandler = prev;
m_start = m_thisMessageOutput->length();
}

View File

@ -0,0 +1,43 @@
#pragma once
#include <QQuickItem>
#include <QtGlobal>
#include <memory>
#include <mutex>
///
/// \brief Monitor output for tests and declaratively control message handler availability
///
/// The captured buffer is global and each instance has a reference to it and a start pointer
/// from its creation or last clear call
/// The first instance installs a QT message handler @see Qt::qInstallMessageHandler then
/// All other instances share the global buffer until the last instance goes out of scope and deregisters
/// from Qt's global message handler and destroyes the buffer
///
/// \todo Check that QML doesn't keep instance between test runs
///
class MonitorQtOutput : public QQuickItem
{
Q_OBJECT
public:
MonitorQtOutput();
~MonitorQtOutput();
/// Return captured output from the global buffer. That is from the instantiation or last `clear()` was called
Q_INVOKABLE QString qtOuput();
/// Reset buffer start after the last line. qtOutput won't return anything until new output is captured
Q_INVOKABLE void restartCapturing();
signals:
private:
static void qtMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg);
static QtMessageHandler m_previousHandler;
// Use it to keep track of qInstallMessageHandler call
static std::weak_ptr<QString> m_qtMessageOutputForSharing;
static std::mutex m_mutex;
std::shared_ptr<QString> m_thisMessageOutput;
int m_start = 0;
};