I have a subproject where I put all my QTest unit tests and build a stand-alone test application that runs the tests (i.e. I run it from within Qt Creator). I have multiple test classes that I can execute with qExec(). However I don't know what is the proper way to execute multiple test classes.
Currently I do it in this way (MVCE):
tests.pro
QT -= gui
QT += core \
testlib
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
TARGET = testrunner
HEADERS += test_foo.h
SOURCES += main.cpp
main.cpp
#include <QtTest>
#include <QCoreApplication>
#include "test_foo.h"
int main(int argc, char** argv) {
QCoreApplication app(argc, argv);
TestFooClass testFoo;
TestBarClass testBar;
// NOTE THIS LINE IN PARTICULAR.
return QTest::qExec(&testFoo, argc, argv) || QTest::qExec(&testBar, argc, argv);
}
test_foo.h
#include <QtTest>
class TestFooClass: public QObject
{
Q_OBJECT
private slots:
void test_func_foo() {};
};
class TestBarClass: public QObject
{
Q_OBJECT
private slots:
void test_func_bar() {};
};
However the documentation for qExec() says this is the wrong way:
For stand-alone test applications, this function should not be called more than once, as command-line options for logging test output to files and executing individual test functions will not behave correctly.
The other major downside is that there is no single summary for all the test classes, only for individual classes. This is a problem when I have dozens of classes that each have dozens of tests. To check if all tests passed I have to scroll up to see all the "Totals" of what passed/failed of each class, e.g.:
********* Start testing of TestFooClass *********
PASS : TestFooClass::initTestCase()
PASS : TestFooClass::test_func_foo()
PASS : TestFooClass::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted
********* Finished testing of TestFooClass *********
********* Start testing of TestBarClass *********
PASS : TestBarClass::initTestCase()
PASS : TestBarClass::test_func_bar()
PASS : TestBarClass::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted
********* Finished testing of TestBarClass *********
I'm also surprised my qExec() || qExec() works considering that the documentation says if a test failed qExec() returns a non-zero value, which should mean all the following qExec() calls wouldn't happen, but this seems not to be the case.
What is the proper way to run multiple test classes? And so that I can see at a glance if any of the hundreds of unit tests I have have failed.
I once found a nice solution using a plain Qt project (no TEMPLATE = subdirs) which uses a macro approach for creating the main function and automatic registering of all test classes (macro, too) with only a simple helper header file.
Here is a sample test class (only the relevant header file):
#ifndef FOOTESTS_H
#define FOOTESTS_H
#include "AutoTest.h"
class FooTests : public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void test1();
void test2();
void cleanupTestCase();
};
DECLARE_TEST(FooTests)
#endif // FOOTESTS_H
and the main, which consumes every test class created this way:
#include "AutoTest.h"
TEST_MAIN
The code of AutoTest.h:
#ifndef AUTOTEST_H
#define AUTOTEST_H
#include <QTest>
#include <QList>
#include <QString>
#include <QSharedPointer>
namespace AutoTest
{
typedef QList<QObject*> TestList;
inline TestList& testList()
{
static TestList list;
return list;
}
inline bool findObject(QObject* object)
{
TestList& list = testList();
if (list.contains(object))
{
return true;
}
foreach (QObject* test, list)
{
if (test->objectName() == object->objectName())
{
return true;
}
}
return false;
}
inline void addTest(QObject* object)
{
TestList& list = testList();
if (!findObject(object))
{
list.append(object);
}
}
inline int run(int argc, char *argv[])
{
int ret = 0;
foreach (QObject* test, testList())
{
ret += QTest::qExec(test, argc, argv);
}
return ret;
}
}
template <class T>
class Test
{
public:
QSharedPointer<T> child;
Test(const QString& name) : child(new T)
{
child->setObjectName(name);
AutoTest::addTest(child.data());
}
};
#define DECLARE_TEST(className) static Test<className> t(#className);
#define TEST_MAIN \
int main(int argc, char *argv[]) \
{ \
return AutoTest::run(argc, argv); \
}
#endif // AUTOTEST_H
All credits goes to Rob Caldecott.
Related
Before the assignment operation, the member variable u works fine, see figure 1
After the assignment operation, the data of the member variable u is tampered, see figure 2
Continuing resize operation will trigger an error: read access violation, see figure 3
The development environment is win10 Visual Studio Community 2022 17.0.4 x64 in Debug mode
Compiling by mingw does not show this problem and the code runs smoothly. I suspect it's a bug for msvc. In addition, this error is very strange, if merged the 4 code files, this error may or may not disappear, which is related to the code order.
Below is the complete code
test.h
#pragma once
class ETEST {
public:
ETEST() {};
void add() {};
};
order.h
#pragma once
#include <vector>
class ETEST;
class First {
public:
First(const char*);
void (ETEST::* add)();
std::vector<double> u, v;
};
class Second : public First {
public:
Second(const char*);
};
first.cpp
#include "test.h"
#include "order.h"
First::First(const char*) :
add(NULL), u{}, v{} {
};
int main(int argc, char* argv[]) {
Second chk("");
return 0;
}
second.cpp
#include "order.h"
#include "test.h"
#include <cstdio>
Second::Second(const char*) :
First(NULL) {
add = &ETEST::add;
u.resize(10, 0.0);
printf("u resize sucess!\n");
};
Below are my actions:
1.create new solution
2.add code files
3,paste the code
4.Debugging, it throws an exception
(I did not modify any solution properties, all use the default settings)
initial question
While testing with googletest, I use a mocked class, which contains an overloaded method, where one of the arguments differs in type.
This leads to an ambiguous function call when I do
EXPECT_CALL(mockedClass,mockedMethod(_,_)).WillOnce(Return(true));
I tried to combine the internal::AnythingMatcher underscore with a type specification by replacing the argument in question with An<type>(), TypedEq<type>(_), or Matcher<type>(_) as well as writing something like:
Matcher<type> customAnythingMatcher = _;
EXPECT_CALL(mockedClass,mockedMethod(_,customAnythingMatcher)).WillOnce(Return(true));
all to no avail.
Any hints how to disambiguate that call?
update #1
As requested, I elaborate by providing a minimal working -- or in this case a non-working -- example what I am trying to achieve:
myInterfaceToMock.h
#ifndef MYINTERFACETOMOCK_H
#define MYINTERFACETOMOCK_H
#include <QVariant>
#include <QtGlobal>
class MyInterfaceToMock {
virtual bool myMethod(QVariant &firstArgument,
const quint8 secondArgument) const = 0;
virtual bool myMethod(QByteArray &firstArgument,
const quint16 secondArgument) const = 0;
virtual bool myOtherMethod(quint8 firstAndOnlyArgument) const = 0;
};
#endif // MYINTERFACETOMOCK_H
myMock.h
#ifndef MYMOCK_H
#define MYMOCK_H
#include <gmock/gmock.h>
#include <QtCore/QObject>
#include "myInterfaceToMock.h"
class MyMock : public MyInterfaceToMock {
public:
// virtual bool myMethod(QVariant &firstArgument, const quint8 secondArgument)
// const = 0;
MOCK_METHOD(bool, myMethod, (QVariant &, const quint8), (const, override));
// virtual bool myMethod(QByteArray &firstArgument, const quint16
// secondArgument) const = 0;
MOCK_METHOD(bool, myMethod, (QByteArray &, const quint16), (const, override));
// virtual bool myOtherMethod(quint8 firstAndOnlyArgument) const = 0;
MOCK_METHOD(bool, myOtherMethod, (quint8), (const, override));
};
#endif // MYMOCK_H
main.cpp
#include <gmock/gmock-matchers.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <QCoreApplication>
#include <QTimer>
class Tester : public QObject {
Q_OBJECT
public:
Tester(QObject *parent = nullptr) : QObject(parent) {}
public slots:
void run() {
::testing::InitGoogleTest();
emit(result(RUN_ALL_TESTS()));
}
signals:
void result(int res);
};
#include "main.moc"
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
::testing::InitGoogleMock(&argc, argv);
Tester *tester = new Tester(&app);
QObject::connect(tester, &Tester::result, &app, &QCoreApplication::exit);
QTimer::singleShot(0, tester, SLOT(run()));
return app.exec();
}
#include "myMock.h"
TEST(myTest, testMockOverload) {
using namespace testing;
MyMock mock;
// Reference method, which does work all the time
EXPECT_CALL(mock, myOtherMethod(_)).WillRepeatedly(Return(false));
// Using a defined value as argument
QVariant myVariant;
EXPECT_CALL(mock, myMethod(myVariant, 3)).WillOnce(Return(true));
bool result = mock.myMethod(myVariant, 3);
ASSERT_TRUE(result);
// Anything matcher for the unambiguous argument
EXPECT_CALL(mock, myMethod(myVariant, _)).WillOnce(Return(true));
result = mock.myMethod(myVariant, 3);
ASSERT_TRUE(result);
// Trying to match any passed qvariant
EXPECT_CALL(mock, myMethod(An<QVariant>(), _)).WillOnce(Return(true));
}
The compiler error is issued for the last line:
..\googlemock_disambiguate\main.cpp(55): error C2664: 'testing::internal::MockSpec<bool (QByteArray &,quint16)> MyMock::gmock_myMethod(const testing::internal::WithoutMatchers &,const testing::internal::Function<bool (QByteArray &,quint16)> *) const': cannot convert argument 1 from 'testing::Matcher<QVariant>' to 'const testing::Matcher<QVariant &> &'
..\googlemock_disambiguate\main.cpp(55): note: Reason: cannot convert from 'testing::Matcher<QVariant>' to 'const testing::Matcher<QVariant &>'
..\googlemock_disambiguate\main.cpp(55): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
I have a guess that it might have sth. to do with the constness of the method, but I still do not understand the meaning of that error...
Using An<type> is the correct solution.
EXPECT_CALL(mockedClass,mockedMethod(_, An<type>())).WillOnce(Return(true));
If it doesn't work for you, the problem lies elsewhere. Perhaps you can elaborate?
Answering the question myself:
I simply did not specify the type correctly, since I expect a reference to get passed. I always tried to use something like An<type>(), where I should have used An<type&>() instead.
So, based on my above example, the correct line simply is:
EXPECT_CALL(mock, myMethod(An<QVariant&>(), _)).WillOnce(Return(true));
I'm having difficulty interpreting some of my results, which I would expect to behave the same but are not.
I am trying to write a method that returns a function pointer getPtrFn
I have a main.c file reading
#include <iostream>
#include "test.hpp"
int main(int argc, char* argv[]){
Test test;
void (*fPtr)(void) = test.getPtrFn();
return 0;
}
A test.hpp file that reads
#ifndef _test_h
#define _test_h
class Test {
private:
void (*ptrFn)(void);
public:
Test(){};
void (*getPtrFn(void))(void){
return ptrFn;
};
~Test();
};
#endif
And a test.cpp file that reads
#include "test.hpp"
Test::~Test(){}
This runs fine. However, when I move the implementation for *getPtrFn(void) to the implementation file (revised files shown below),
test.hpp:
#ifndef _test_h
#define _test_h
class Test {
private:
void (*ptrFn)(void);
public:
Test(){};
void (*getPtrFn(void))(void);
~Test();
};
#endif
test.cpp:
#include "test.hpp"
void (Test::*getPtrFn)(void){
return ptrFn;
};
Test::~Test(){}
I get the compile error
test.cpp:16:9: error: use of undeclared identifier 'ptrFn'
My understanding of the language syntax is that they would be treated the same. So what gives?
-Jeff
You need
void(*Test::getPtrFn(void))(void)
{
return ptrFn;
}
instead of void (Test::*getPtrFn)(void){...}. void (Test::*getPtrFn)(void) is the declaration of getPtrFn as a pointer-to-Test-member-function taking no parameters (void) and returning void, so after you put the braces { ... } you get a compile-time error (its like trying to declare int i{/*some statemets*/}).
Also, and don't forget to keep the declaration
void(*getPtrFn(void))(void);
in your header (right now it seems you don't have it, did you cut/pasted it?).
Quite a horrible thing to look at... So really, use a type alias, it makes your code much cleaner.
using PTRFN = void(*)(void); // or typedef void(*PTRFN)(void);
class Test {
private:
PTRFN ptrFn;
public:
PTRFN getPtrFn(void);
Test(){};
~Test(){};
};
PTRFN Test::getPtrFn(void) // clear an concise
{
return ptrFn;
}
In case you really really want to be able do decipher every kind of pointer declaration you can think of, try looking at the clockwise/spiral rule, I found it extremely useful, clear and easy to understand. Then test your knowledge at cdecl.org.
I am new to Dbus, and off course QDBUS. I am trying to replicate the example from the nokia developer -QT forum. I have a xml file, through which I have generated the qdbus interface adaptor.cpp and .h. Now, I am trying to include this file, and build it. However, I get the compilation error. Can you please him to fix this one?
(.text.startup+0x4c):-1: error: undefined reference to `MyDemo::MyDemo(QObject*)'
generated- adaptor.cpp
/*
* This file was generated by qdbusxml2cpp version 0.7
* Command line was: qdbusxml2cpp -c DemoIfAdaptor -a demoifadaptor.h:demoifadaptor.cpp com.nokia.Demo.xml
*
* qdbusxml2cpp is Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
*
* This is an auto-generated file.
* Do not edit! All changes made to it will be lost.
*/
#include "demoifadaptor.h"
#include <QtCore/QMetaObject>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
/*
* Implementation of adaptor class DemoIfAdaptor
*/
DemoIfAdaptor::DemoIfAdaptor(QObject *parent)
: QDBusAbstractAdaptor(parent)
{
// constructor
setAutoRelaySignals(true);
}
DemoIfAdaptor::~DemoIfAdaptor()
{
// destructor
}
void DemoIfAdaptor::SayBye()
{
// handle method call com.nokia.Demo.SayBye
QMetaObject::invokeMethod(parent(), "SayBye");
}
void DemoIfAdaptor::SayHello(const QString &name, const QVariantMap &customdata)
{
// handle method call com.nokia.Demo.SayHello
QMetaObject::invokeMethod(parent(), "SayHello", Q_ARG(QString, name), Q_ARG(QVariantMap, customdata));
}
Generated Adaptor.h
/*
* This file was generated by qdbusxml2cpp version 0.7
* Command line was: qdbusxml2cpp -c DemoIfAdaptor -a demoifadaptor.h:demoifadaptor.cpp com.nokia.Demo.xml
*
* qdbusxml2cpp is Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
*
* This is an auto-generated file.
* This file may have been hand-edited. Look for HAND-EDIT comments
* before re-generating it.
*/
#ifndef DEMOIFADAPTOR_H_1392803889
#define DEMOIFADAPTOR_H_1392803889
#include <QtCore/QObject>
#include <QtDBus/QtDBus>
class QByteArray;
template<class T> class QList;
template<class Key, class Value> class QMap;
class QString;
class QStringList;
class QVariant;
/*
* Adaptor class for interface com.nokia.Demo
*/
class DemoIfAdaptor: public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "com.nokia.Demo")
Q_CLASSINFO("D-Bus Introspection", ""
" <interface name=\"com.nokia.Demo\">\n"
" <method name=\"SayHello\">\n"
" <annotation value=\"QVariantMap\" name=\"com.trolltech.QtDBus.QtTypeName.In1\"/>\n"
" <arg direction=\"in\" type=\"s\" name=\"name\"/>\n"
" <arg direction=\"in\" type=\"a{sv}\" name=\"customdata\"/>\n"
" </method>\n"
" <method name=\"SayBye\"/>\n"
" <signal name=\"LateEvent\">\n"
" <arg direction=\"out\" type=\"s\" name=\"eventkind\"/>\n"
" </signal>\n"
" </interface>\n"
"")
public:
DemoIfAdaptor(QObject *parent);
virtual ~DemoIfAdaptor();
public: // PROPERTIES
public Q_SLOTS: // METHODS
void SayBye();
void SayHello(const QString &name, const QVariantMap &customdata);
Q_SIGNALS: // SIGNALS
void LateEvent(const QString &eventkind);
};
#endif
main.cpp file -
#include <QtCore/QCoreApplication>
#include <QVariant>
#include <QDBusAbstractInterface>
#include "demoifadaptor.h"
class MyDemo : public QObject
{
Q_OBJECT
public:
explicit MyDemo(QObject *parent = 0);
public Q_SLOTS:
void SayBye();
void SayHello(const QString &name, const QVariantMap &customdata);
Q_SIGNALS:
void LateEvent(const QString &eventkind);
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyDemo* demo = new MyDemo;
new DemoIfAdaptor(demo);
QDBusConnection connection = QDBusConnection::sessionBus();
bool ret = connection.registerService("com.nokia.Demo");
ret = connection.registerObject("/", demo);
return a.exec();
}
MyDemo.pro file.
#-------------------------------------------------
#
# Project created by QtCreator 2014-02-19T15:30:36
#
#-------------------------------------------------
QT += core
QT -= gui
QT += dbus
TARGET = MyDemo
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp \
demoifadaptor.cpp
HEADERS += \
demoifadaptor.h
Your example presents a declaration of explicit MyDemo(QObject *parent = 0); but doesn't supply it's definition. Thus the linker error. The rest of MyDemo methods must be defined as well.
You also need to run the Meta Object Compiler on the source file to avoid
"vtable..."
errors. Try re-run qmake. Any time you add a new call to the Q_OBJECT macro, you need to run qmake again. The vtables issue you're referring to in comments is directly related to that.
In case error still persist add #include "MyDemo.moc" at the end of main.cpp
If you check the qnamespace.h from Qt source code, you get to see something like this:
#ifndef Q_MOC_RUN
namespace
#else
class Q_CORE_EXPORT
#endif
Qt {
#if defined(Q_MOC_RUN)
Q_OBJECT
#endif
#if (defined(Q_MOC_RUN) || defined(QT_JAMBI_RUN))
// NOTE: Generally, do not add Q_ENUMS if a corresponding Q_FLAGS exists.
Q_ENUMS(ScrollBarPolicy FocusPolicy ContextMenuPolicy)
Q_ENUMS(ArrowType ToolButtonStyle PenStyle PenCapStyle PenJoinStyle BrushStyle)
Q_ENUMS(FillRule MaskMode BGMode ClipOperation SizeMode)
Q_ENUMS(BackgroundMode) // Qt3
My interpretation of this code is, that the moc preprocessor is fooled into generating meta-type information for a fake Qt object. How can I access this "fake" meta-object to get, for example, a QMetaEnum for ArrowType and other enums?
The code below does it. The output is:
LeftArrow
#include <QtCore/QTextStream>
#include <QtCore/QMetaEnum>
struct StaticQtMetaObject : public QObject
{
static inline const QMetaObject& get() {return staticQtMetaObject;}
};
int main(int argc, char *argv[])
{
const QMetaObject& mo = StaticQtMetaObject::get();
int index = mo.indexOfEnumerator("ArrowType");
QMetaEnum me = mo.enumerator(index);
Qt::ArrowType arrowType = Qt::LeftArrow;
QTextStream(stdout) << me.valueToKey(arrowType) << endl;
return 0;
}
Courtesy of http://qt-project.org/forums/viewthread/658