Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Cannot get QtTest to verify an signal parameter



  • Hello. I'm trying to verify my application code, a card game, using QtTest. One thing I'm doing is emitting a signal when a card is turned face up or down.

    class Card : public QObject
    {
    		Q_OBJECT
    
    	public:
    		...
    		enum Orientation { FACE_UP, FACE_DOWN };
    		Q_ENUM(Orientation)
    		Q_PROPERTY(Orientation orientation
    		           READ getOrientation
    		           WRITE setOrientation
    		           NOTIFY orientationChanged)
    		Orientation getOrientation() const;
    		void setOrientation(Orientation orientation);
    
    	signals:
    		void orientationChanged(Orientation orientation);
    }
    
    Q_DECLARE_METATYPE(Card::Orientation);
    
    

    The signal "orientationChanged" includes an enum of type "Orientation" to indicate if the card has been turned up or down.

    In my module tests, I turn the card up and down, and can verify that the number of signals are increasing using QSignalSpy. However, I cannot figure out how to verify that the included parameter is FACE_UP or FACE_DOWN.

    	qRegisterMetaType<Card::Orientation>("Card::Orientation");
    	QSignalSpy spyCard_2_spades(card_2_Spades, SIGNAL(orientationChanged(Orientation)));
    	QVERIFY(spyCard_2_spades.count() == 0);
    
    

    I've seen messages that say to use qRegisterMetaType, so I've added that. Still, I get a warning about QSignalSpy cannot handle the parameter orientation.

    ********* Start testing of ModuleTests *********
    Config: Using QtTest library 5.9.5, Qt 5.9.5 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 7.3.0)
    PASS   : ModuleTests::initTestCase()
    QWARN  : ModuleTests::test_Card() QSignalSpy: Unable to handle parameter 'orientation' of type 'Orientation' of method 'orientationChanged', use qRegisterMetaType to register it.
    PASS   : ModuleTests::test_Card()
    PASS   : ModuleTests::cleanupTestCase()
    Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted, 0ms
    ********* Finished testing of ModuleTests *********
    

    I see examples online, but using a custom type seems to bring its own issues. Can anyone tell me what I'm doing wrong, or point me to an example that handles custom data types?

    BTW, if you want to see all the code in context, it's on github at git@github.com:bob-weber/cardGame-500.git.

    Thanks everyone.


  • Lifetime Qt Champion

    Hi @gaylord, welcome!

    Unfortunately your github repo is empty except the readme...

    You are alread using Q_ENUM, which is good. You can change the Q_DECLARE_METATYPE to:

    Q_DECLARE_METATYPE(Card::Orientation, Q_PRIMITIVE_TYPE);

    qRegisterMetaTypeCard::Orientation("Card::Orientation");
    QSignalSpy spyCard_2_spades(card_2_Spades, SIGNAL(orientationChanged(Orientation)));
    QVERIFY(spyCard_2_spades.count() == 0);

    I'd try two things here:

    1. qRegisterMetaType<Card::Orientation>(); should be enough
    2. QSignalSpy spyCard_2_spades(card_2_Spades, &Card::orientationChanged); to establish the connection at compile time

    I hope one of these steps brings the solution. Regards


  • Qt Champions 2019

    The SIGNAL declaration is wrong. It must contain the fully qualified type:

    void orientationChanged(Card::Orientation orientation);

    because the old Signal/Slot syntax is using string comparison.
    If you use the new Signal/Slot syntax as @aha_1980 mentioned the problem should also go away.



  • Thanks aha_1980 and Chrisitan.

    Sorry about the code not being in github. I guess I didn't push it. Now I have.

    Christian, why do I need to fully qualify the type in the signal definition? It's defined in the class, so I would think that's enough. Is it because signals are used in a global fashion, and where they're defined isn't preserved when used by the system?

    I had trouble getting aha_1980's approach to work, but adding the fully qualified type to the signal parameter did make the error go away.

    I've updated the code to retrieve the parameters, but now I'm running into a problem with the QSignalSpy.count(). It's getting reset to 0. Maybe retrieving the arguments with "spyCard_2_spaces.takeAt() resets the signal count? I thought count() would return the total # of signals generated, and I could use that as an index to retrieve the arguments, but I'm obviously not understanding this.

    	unsigned int signalCount;
    	QList<QVariant> arguments;
    
    	// Verify no signals have been emitted for the 2 of spades
    	qRegisterMetaType<Card::Orientation>();
    	QSignalSpy spyCard_2_spades(card_2_Spades, &Card::orientationChanged);
    	signalCount = spyCard_2_spades.count();
    	QVERIFY(signalCount == 0);
    
    	// Set card face down; it should currently be face up. Signal should be emitted.
    	card_2_Spades->setOrientation(Card::FACE_DOWN);
    	signalCount = spyCard_2_spades.count();
    	// Arguments for this signal event are 0 based, so the 1st signal event's arguments are at QList[0].
    	arguments = spyCard_2_spades.takeAt(signalCount-1);
    	QVERIFY(card_2_Spades->getOrientation() == Card::FACE_DOWN);
    	QVERIFY(signalCount == 1);
    	QVERIFY(arguments.at(0) == Card::FACE_DOWN);
    
    	// Set card face down again. They're already face down, so no signal should be emitted.
    	card_2_Spades->setOrientation(Card::FACE_DOWN);
    	signalCount = spyCard_2_spades.count();
    	/ ERROR HERE. count returns 0. I didn't expect the counter to get reset.
    	// This causes the next call to seg fault.
    	arguments = spyCard_2_spades.takeAt(signalCount-1);;
    	QVERIFY(card_2_Spades->getOrientation() == Card::FACE_DOWN);
    	QVERIFY(spyCard_2_spades.count() == 1);
    	QVERIFY(arguments.at(0) == Card::FACE_DOWN);
    

  • Qt Champions 2019

    @gaylord said in Cannot get QtTest to verify an signal parameter:

    Christian, why do I need to fully qualify the type in the signal definition?

    I already answered it:

    because the old Signal/Slot syntax is using string comparison.



  • I guess I don't quite understand what "using string comparison" means. I did some searching and found this thread, and it has the comment:

    For the record, the reason why this solves the problem is that the metaobject system is deciding signal/slot compatibility based on (normalized) string comparisons. It doesn't "know" that StateMachine::state and state refer to the same type in this context. – rohanpm Sep 12 '12 at 1:26
    

    I'll accept this as "that's the way it works", with a loose belief that it's because signals and slots are parsed as strings in a global context, and where they're declared is not available to the parser. As I learn more about Qt, I expect I'll understand this better and refine my belief to something more accurate.


  • Lifetime Qt Champion

    Hi @gaylord,

    please have a look at https://wiki.qt.io/New_Signal_Slot_Syntax for the "new" syntax, which is checked at compile time.

    The "old-style" connect however was done at runtime, by simply comparing strings of SIGNAL and SLOT. I could therefore happen that you renamed one of them and the connect in turn no longer worked.

    Regards



  • @gaylord as mentioned before, you may want to switch to the new syntax for signals and slots.
    This answer is even proposing getting rid of QSignalSpy by just using connect with a lambda function.


Log in to reply