PDA

View Full Version : Trouble using custom datatype (QMetaType) in QtTest



perden
28th August 2009, 15:39
I have a compilation error related to the QMetaType and QtTest. I feel like my issue is rooted in some misunderstanding I have about a Qt concept. Skip below for the complication error, or start here for the full story.

I am trying to write a unit test for a class that I have written, CustomVector, which is a QVector over a QHash of QString to QVariant. I attach this to the metatyping system with the Q_DECLARE_METATYPE() macro after the class definiton. In the same header file, I have a TestCustomVector class which is intended to test the CustomVector. This class is encased in #ifdefs to only compile in debug configuration.

customvector.h


// ... necessary includes here ...

class CustomVector : public QVector< QHash<QString, QVariant> > { ... };
Q_DECLARE_METATYPE(CustomVector)

#ifdef QT_DEBUG

#include <QtTest>

class TestCustomVector : public QObject
{
Q_OBJECT

private slots:
void isNull_data();
void isNull();
};

#endif // QT_DEBUG


The CustomVector and TestCustomVector classes are also implemented in the same cpp file. TestCustomVector has the one isNull() test which is implemented using the data-driven testing methodology. Again, the unit testing code is only compiled in on a debug build.

customvector.cpp


// ... CustomVector functions including an isNull() function ...

#ifdef QT_DEBUG

void TestCustomVector::isNull_data()
{
QTest::addColumn<CustomVector>("CustomVector");
QTest::addColumn<bool>("Result");

CustomVector cv_default(); // will be null
CustomVector cv_notNull(/* ... parameters giving a not null state... */);
CustomVector cv_null(/* ... parameters giving a null state... */)

QTest::newRow("Default Constructor")
<< &cv_default << true;
QTest::newRow("Basic Constructor, Fully Specified")
<< &cv_notNull << false;
QTest::newRow("Basic Constructor, Partially Specified")
<< &cv_nameNull << true;
}

void TestCustomVector::isNull()
{
QFETCH(CustomVector, customVector);
QFETCH(bool, result);

QCOMPARE(customVector, result);
}

#endif // QT_DEBUG


These tests are defined in the files they refer to for ease of coding. I also intend to call QTest::qExec() in my main.cpp as a program command-line option of the main program.

When compiling, I get the following errors:

/opt/qt-4.5.2/include/QtCore/qmetatype.h: In static member function ‘static int QMetaTypeId2<T>::qt_metatype_id() [with T = CustomVector (*)()]’:
/opt/qt-4.5.2/include/QtCore/qmetatype.h:199: instantiated from ‘int qMetaTypeId(T*) [with T = CustomVector (*)()]’
/opt/qt-4.5.2/include/QtTest/qtestdata.h:82: instantiated from ‘QTestData& operator<<(QTestData&, const T&) [with T = CustomVector (*)()]’
customvector.cpp:15: instantiated from here
/opt/qt-4.5.2/include/QtCore/qmetatype.h:189: error: ‘qt_metatype_id’ is not a member of ‘QMetaTypeId<CustomVector (*)()>’
/opt/qt-4.5.2/include/QtCore/qmetatype.h: In static member function ‘static int QMetaTypeId2<T>::qt_metatype_id() [with T = CustomVector*]’:
/opt/qt-4.5.2/include/QtCore/qmetatype.h:199: instantiated from ‘int qMetaTypeId(T*) [with T = CustomVector*]’
/opt/qt-4.5.2/include/QtTest/qtestdata.h:82: instantiated from ‘QTestData& operator<<(QTestData&, const T&) [with T = CustomVector*]’
customvector.cpp:17: instantiated from here
/opt/qt-4.5.2/include/QtCore/qmetatype.h:189: error: ‘qt_metatype_id’ is not a member of ‘QMetaTypeId<CustomVector*>’

Based on other threads I have searched, I have tried:

Separating the code into individual files
Using Q_DECLARE_METATYPE(CustomVector *) and Q_DECLARE_METATYPE(CustomVector)
Various permutations of streaming objects, pointers, and references into QTest::newRow()

Like I said before, I feel like whatever I am missing is a high-level Qt concept. Perhaps someone can shed some holy Qt wisdom on me? :D

perden
1st September 2009, 17:08
I've made some changes and I now get a slightly different result. I've changed my earlier code around such that the metatype is a pointer to a CustomVector and commented out the default constructor as below.

customvector.h


// ... necessary includes here ...

class CustomVector : public QVector< QHash<QString, QVariant> > { ... };
Q_DECLARE_METATYPE(CustomVector *) // <-- added pointer symbol

#ifdef QT_DEBUG

#include <QtTest>

class TestCustomVector : public QObject
{
Q_OBJECT

private slots:
void isNull_data();
void isNull();
};

#endif // QT_DEBUG

customvector.cpp


// ... CustomVector functions including an isNull() function ...

#ifdef QT_DEBUG

void TestCustomVector::isNull_data()
{
QTest::addColumn<CustomVector *>("CustomVector"); // <-- added pointer symbol
QTest::addColumn<bool>("Result");

// CustomVector cv_default(); // <-- commented out
CustomVector cv_notNull(/* ... parameters giving a not null state... */);
CustomVector cv_null(/* ... parameters giving a null state... */)

// QTest::newRow("Default Constructor") // <-- commented out
// << &cv_default << true;
QTest::newRow("Basic Constructor, Fully Specified")
<< &cv_notNull << false;
QTest::newRow("Basic Constructor, Partially Specified")
<< &cv_null << true; // <-- corrected this line from earlier post
}

void TestCustomVector::isNull()
{
QFETCH(CustomVector *, customVector); // <-- added pointer symbol
QFETCH(bool, result);

QCOMPARE(customVector, result);
}

#endif // QT_DEBUG

The entire program compiles and I have the following linker errors. Note that the CustomVector is part of a library with the same name.


libcustomvector.a(customvector.o): In function `TestCustomVector::isNull()':
customvector/customvector.cpp:108: undefined reference to `bool QTest::qCompare<CustomVector*, bool>(CustomVector* const&, bool const&, char const*, char const*, char const*, int)'
collect2: ld returned 1 exit status

I don't think this is progress because I don't think it's appropriate for an implicitly-shared class like CustomVector (extending QVector) to be metatype'd as a pointer.

What is going on here?

caduel
1st September 2009, 18:04
QCOMPARE(customVector, result);
this code compares a CustomVector* to a bool (using QTest::qCompare; as qCompare is declared as a template function, this does indeed compile). It does not link, however, as qCompare<> is not specialized for those types.
(And probably you did not want to compare the pointer to a bool, did you? Doesn't make too much sense.)

perden
1st September 2009, 21:29
Ok! Progress!

Thanks caduel, your comment was what I needed to get quite a bit further. I've once again, removed the pointer symbols from around the files, renamed the columns to be identical to the variables they reference, and added the .isNull() to the QTest::QCOMPARE().

customvector.h


// ... necessary includes here ...

class CustomVector : public QVector< QHash<QString, QVariant> > { ... };
Q_DECLARE_METATYPE(CustomVector) // <-- removed pointer symbol

#ifdef QT_DEBUG

#include <QtTest>

class TestCustomVector : public QObject
{
Q_OBJECT

private slots:
void isNull_data();
void isNull();
};

#endif // QT_DEBUG



customvector.cpp


// ... CustomVector functions including an isNull() function ...

#ifdef QT_DEBUG

void TestCustomVector::isNull_data()
{
QTest::addColumn<CustomVector>("customVector"); // <-- removed pointer symbol
QTest::addColumn<bool>("result");
// changed both above strings to match the variable names in the QFETCH() in TestCustomVector::isNull()

// CustomVector cv_default(); // <-- still commented out
CustomVector cv_notNull(/* ... parameters giving a not null state... */);
CustomVector cv_null(/* ... parameters giving a null state... */)

// QTest::newRow("Default Constructor") // <-- still commented out
// << cv_default << true;
QTest::newRow("Basic Constructor, Fully Specified")
<< cv_notNull << false;
QTest::newRow("Basic Constructor, Partially Specified")
<< cv_null << true;
// removed & operators in above cv_* variables
}

void TestCustomVector::isNull()
{
QFETCH(CustomVector, customVector); // <-- removed pointer symbol
QFETCH(bool, result);

QCOMPARE(customVector.isNull(), result); // <-- added .isNull()
}

#endif // QT_DEBUG


I guess was assuming that QTest::QCOMPARE() implicitly called the function you wanted to test based on the name of the function itself, effectively tacking-on the ".isNull()" to my CustomVector call. This is wrong.

I've removed the pointers to take advantage of those implicitly-shared classes.

I'm really close now. Everything builds and links fine, so long as the default constructor and related test case remain commented in TestCustomVector::isNull_data().

Any other remarks as to why this might not be working?

caduel
2nd September 2009, 07:40
please elaborate on what "not working" means here

perden
2nd September 2009, 14:59
All fixed!

I was getting a compilation error for the default constructor on line 11 of the most recent testcustomvector.cpp example. This was due to the unnecessary parenthesis after the name of the variable. That's my early java background sneaking in again!

So in the end, it turns out that my problems were caused by a combination of lack of QtTest knowledge and a C++ misunderstanding from my early java background.

Thanks again for the help.

caduel
2nd September 2009, 15:26
ah, ok...

CustomVector cv_default();
in C(++) declares a function named cv_default, taking no arguments and returning a CustomVector.

Line 11 should compile. However, your use of cv_default later on would give trouble.