Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QtTest Testing Class with delayed event
QtWS25 Last Chance

QtTest Testing Class with delayed event

Scheduled Pinned Locked Moved Solved General and Desktop
11 Posts 2 Posters 910 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    sandro4912
    wrote on 22 Oct 2019, 17:11 last edited by sandro4912
    #1

    I try to get into the unit test framework from Qt. So i thought it would be a good idea to write tests for the minesweeper clone i wrote.

    However i ran into trouble getting one test to pass:

    I wanto test in my Cell Class if the Signal flagged is emitted:

    class Cell : public QWidget
    {
        Q_OBJECT
    public:
    //....
    
        void handleMousePressEvent(QMouseEvent *event);
    
    //....
    
    signals:
        void flagged();
    //....
    };
    

    Flagged is normally emitted if the User does a right click on the widget, after it was constructed.

    I tryed to test it like this:

    void TestCell::flagged()
    {
        QFETCH(Cell::State, cellState);
        QFETCH(int, result);
    
        Cell obj{ cellState };
        QSignalSpy spy(&obj, &Cell::flagged);
    
        pressRightMouseButton(obj);
        // wait here until really counts as pressed right?
    
        QCOMPARE(spy.count(), result);
    }
    

    The strange pressRightMouseButton function does this:

    void pressRightMouseButton(Cell &cell)
    {
        QMouseEvent pressRightButton{
            QEvent::MouseButtonPress,
            cell.rect().center(),
            Qt::RightButton,
            Qt::RightButton,
            Qt::NoModifier
        };
        cell.handleMousePressEvent(&pressRightButton);
    }
    

    handleMousePressEvent is supposed to be used from a Event Filter class which gets installed in all the Cells. So i simulate its call here. This is to handle other actions like holding both mouse buttons down and slide from one widget into annother.

    However now to my main problem. The flagged signal is normally emitted like this in the Cell class:

    void Cell::handleMousePressEvent(QMouseEvent *event)
    {
        if(!(event->buttons().testFlag(Qt::LeftButton) ||
             event->buttons().testFlag(Qt::RightButton))) {
            return;
        }
        const auto elapsedTime = mElapsedTimer.restart();
    
        if(elapsedTime < QApplication::doubleClickInterval()){
            if(event->buttons().testFlag(Qt::LeftButton) &&
                    event->buttons().testFlag(Qt::RightButton)) {
    
                if(!isPressed()) {
                    pressIfReleased();
                    mNeighboursPressed = true;
                    emit pressNeighbours();
                }
            }
            for(QTimer* timer : { &mSingleMouseTimerRight,
                &mSingleMouseTimerLeft }) {
                timer->stop();
            }
            return;
        }
        if(event->buttons().testFlag(Qt::LeftButton)) {
            mSingleMouseTimerLeft.start();
        }
        else {
            mSingleMouseTimerRight.start();
        }
    }
    

    In the constructor:

    constexpr auto intervall = 50;
       for(QTimer* timer : {&mSingleMouseTimerRight, &mSingleMouseTimerLeft}){
           timer->setInterval(intervall);
           timer->setSingleShot(true);
       }
    
       connect(&mSingleMouseTimerLeft, &QTimer::timeout,
               this, &Cell::leftMouseButtonWasClicked);
       connect(&mSingleMouseTimerRight, &QTimer::timeout,
               this, &Cell::rightMouseButtonWasClicked);
    

    So if 50ms pass it counts as right of left clicked and this is called:

    void Cell::rightMouseButtonWasClicked()
    {
        mark();
    }
    
    void Cell::mark()
    {
        switch (mDisplayType) {
        case DisplayType::covered:
            mDisplayType = DisplayType::flagged;
            emit flagged();
            update();
            break;
        case DisplayType::flagged:
            if(mQuestionMarksOn) {
                mDisplayType = DisplayType::questionmark;
            }
            else {
                mDisplayType = DisplayType::covered;
            }
            emit unflagged();
            update();
            break;
        case DisplayType::questionmark:
            mDisplayType = DisplayType::covered;
            update();
            break;
        default:
            break;
        }
    }
    

    My problem is the test above seems not to pass because the 50ms delay is never reached. But how can i reach it that rightMouseButtonWasClicked is triggered?

    I tryed out QThread::msleep() but no result with that.

    If my explanation is confusing and you need more information let me know.

    1 Reply Last reply
    0
    • S Offline
      S Offline
      SGaist
      Lifetime Qt Champion
      wrote on 22 Oct 2019, 18:22 last edited by
      #2

      Hi,

      Out of curiosity, why not use QTest::mouseClick ?

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      S 1 Reply Last reply 22 Oct 2019, 18:36
      0
      • S SGaist
        22 Oct 2019, 18:22

        Hi,

        Out of curiosity, why not use QTest::mouseClick ?

        S Offline
        S Offline
        sandro4912
        wrote on 22 Oct 2019, 18:36 last edited by sandro4912
        #3

        @SGaist

        But that only works if i directly override mousePressEvent in the Cell class or doesn't it?

        I cannot do that because in reality the Cell Events get managed from a CellInputHandler eventfilter Class which gets the events before to also detect stuff like move events from one to annother widget.

        That EventFilter normally calls handleMousePressEvent

        More ont this here:

        https://forum.qt.io/topic/107430/detect-if-mouse-buttons-are-pressed-when-sliding-into-widget/8

        So its then used like this:

        bool CellInputHandler::eventFilter(QObject *watched, QEvent *event)
        {
            if(event->type() == QEvent::MouseButtonPress){       
               handleMouseButtonPressEvents(watched, event);
               return true;
            }
            if(event->type() == QEvent::MouseButtonRelease){      
                handleMouseButtonReleaseEvents(watched, event);
               return true;
            }
            if(event->type() == QEvent::MouseMove) {
                handleMouseMoveEvents(event);
                return true;
            }
            return false;
        }
        
        void CellInputHandler::handleMouseButtonPressEvents(
                QObject *watched, QEvent *event)
        {
            auto mouseEvent = static_cast<QMouseEvent*>(event);
            auto cell = qobject_cast<Cell *>(watched);
            cell->handleMousePressEvent(mouseEvent);
        }
        
        1 Reply Last reply
        0
        • S Offline
          S Offline
          SGaist
          Lifetime Qt Champion
          wrote on 22 Oct 2019, 18:38 last edited by
          #4

          Since you are testing a functionality that requires your custom filter, why not install it as part of your test ?
          It would match closer the real situation, no ?

          Interested in AI ? www.idiap.ch
          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

          1 Reply Last reply
          1
          • S Offline
            S Offline
            sandro4912
            wrote on 23 Oct 2019, 17:03 last edited by
            #5

            Yes i guess you are right. I was thinking of making CellInputHandler a Singleton because the same Handler needs to be installed on all Cells. That way i could maybe install it in the constructor of Cell or is that not possible?

            Currently the parent of all Cells holds the Handler and installs it on all the Cells it owns aswell.

            1 Reply Last reply
            0
            • S Offline
              S Offline
              SGaist
              Lifetime Qt Champion
              wrote on 23 Oct 2019, 17:24 last edited by
              #6

              Your current situation is cleaner. There's no need for a singleton.

              Interested in AI ? www.idiap.ch
              Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

              1 Reply Last reply
              0
              • S Offline
                S Offline
                sandro4912
                wrote on 23 Oct 2019, 18:22 last edited by
                #7

                Ok so i adjusted my tests to Test Cell with an installed CellInputHandler.

                To Check for an click and release event there is no issue:

                void TestCell::hitMine_data()
                {
                    QTest::addColumn<Cell::State>("cellState");
                    QTest::addColumn<int>("result");
                    QTest::newRow("has mine") << Cell::State::mine << 1;
                    QTest::newRow("is empty") << Cell::State::empty << 0;
                }
                
                void TestCell::hitMine()
                {
                    QFETCH(Cell::State, cellState);
                    QFETCH(int, result);
                
                    CellInputHandler filter;
                    Cell obj{ cellState };
                    obj.installEventFilter(&filter);
                
                    QSignalSpy spy(&obj, &Cell::hitMine);
                
                    QTest::mouseClick(&obj, Qt::LeftButton, Qt::NoModifier);
                
                    QCOMPARE(spy.count(), result);
                }
                

                This passes. The Widget gets clicked and released and we have the hitMinesignal emited.

                However still doing the right click does not pass:

                void TestCell::flagged_data()
                {
                    QTest::addColumn<Cell::State>("cellState");
                    QTest::addColumn<int>("result");
                    QTest::newRow("has mine") << Cell::State::mine << 1;
                    QTest::newRow("is empty") << Cell::State::empty << 1;
                }
                
                void TestCell::flagged()
                {
                    QFETCH(Cell::State, cellState);
                    QFETCH(int, result);
                
                    CellInputHandler filter;
                    Cell obj{ cellState };
                    obj.installEventFilter(&filter);
                
                    QSignalSpy spy(&obj, &Cell::flagged);
                
                    QTest::mousePress(&obj, Qt::RightButton, Qt::NoModifier);
                    QTest::mouseRelease(&obj, Qt::RightButton, Qt::NoModifier,
                                        obj.rect().center(), 100);
                
                    QCOMPARE(spy.count(), result);
                }
                

                here the signal gets emitted when right was clicked and 50ms are passed (See the Description in my first post).

                So what am i doing wrong here?

                1 Reply Last reply
                0
                • S Offline
                  S Offline
                  SGaist
                  Lifetime Qt Champion
                  wrote on 23 Oct 2019, 18:27 last edited by
                  #8

                  I would go with QTest::qWaitFor.

                  Interested in AI ? www.idiap.ch
                  Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                  S 1 Reply Last reply 23 Oct 2019, 18:42
                  1
                  • S Offline
                    S Offline
                    sandro4912
                    wrote on 23 Oct 2019, 18:33 last edited by sandro4912
                    #9
                    This post is deleted!
                    1 Reply Last reply
                    0
                    • S SGaist
                      23 Oct 2019, 18:27

                      I would go with QTest::qWaitFor.

                      S Offline
                      S Offline
                      sandro4912
                      wrote on 23 Oct 2019, 18:42 last edited by sandro4912
                      #10

                      @SGaist

                      I tryed these two things but both don't work:

                         QTest::mousePress(&obj, Qt::RightButton, Qt::NoModifier,
                                           obj.rect().center());
                      
                         QTest::qWaitFor([]() { return false; }, 3000);
                      
                         QTest::mouseRelease(&obj, Qt::RightButton, Qt::NoModifier,
                                             obj.rect().center(), 100);
                      
                      
                         QCOMPARE(spy.count(), result);
                      

                      and

                         QTest::mousePress(&obj, Qt::RightButton, Qt::NoModifier,
                                           obj.rect().center());
                      
                         QTest::mouseRelease(&obj, Qt::RightButton, Qt::NoModifier,
                                             obj.rect().center(), 100);
                      
                         QTest::qWaitFor([&]() {
                             return spy.count() == 1;
                         }, 3000);
                      
                         QCOMPARE(spy.count(), result);
                      
                      1 Reply Last reply
                      0
                      • S Offline
                        S Offline
                        sandro4912
                        wrote on 24 Oct 2019, 17:06 last edited by sandro4912
                        #11

                        i solved the issue. I found out that only when I add two press statements with a delay after the second the method works correctly.

                        I fixed the bug like this:

                        void Cell::handleMousePressEvent(QMouseEvent *event)
                        {
                            if(!(event->buttons().testFlag(Qt::LeftButton) ||
                                 event->buttons().testFlag(Qt::RightButton))) {
                                return;
                            }
                        
                            if(event->buttons().testFlag(Qt::LeftButton)) {
                                mSingleMouseTimerLeft.start();
                            }
                            else if (event->buttons().testFlag(Qt::RightButton)){
                                mSingleMouseTimerRight.start();
                            }
                        
                            const auto elapsedTime = mElapsedTimer.restart();
                        
                            if(elapsedTime < QApplication::doubleClickInterval() && 
                                    event->buttons().testFlag(Qt::LeftButton) &&
                                    event->buttons().testFlag(Qt::RightButton)) {
                        
                                if(!isPressed()) {
                                    pressIfReleased();
                                    mNeighboursPressed = true;
                                    emit pressNeighbours();
                                }
                                for(QTimer* timer : { &mSingleMouseTimerRight,
                                    &mSingleMouseTimerLeft }) {
                                    timer->stop();
                                }
                                return;
                            }
                        }
                        

                        Before we always slided into elapsedTime < QApplication::doubleClickInterval and then the first time:

                        for(QTimer* timer : { &mSingleMouseTimerRight,
                                  &mSingleMouseTimerLeft }) {
                                  timer->stop();
                              }
                        

                        was triggered erasing the passed time. only with two nice delays i could get it to work. Now after fix it works as expected:

                        void TestCell::flagged()
                        {
                            constexpr auto delayClickRightAndLeftTogether = 50;
                            
                            QFETCH(Cell::State, cellState);
                            QFETCH(int, result);
                        
                            CellInputHandler filter;
                            Cell obj{ cellState };
                            obj.installEventFilter(&filter);
                        
                            QSignalSpy spy(&obj, &Cell::flagged);
                        
                            QTest::mousePress(&obj, Qt::RightButton, Qt::NoModifier,
                                              obj.rect().center());
                        
                            spy.wait(delayClickRightAndLeftTogether);
                        
                            QCOMPARE(spy.count(), result);
                        }
                        

                        So i already found a bug. Thats great.

                        Note: I used spy.wait but i think its like QTest::qWaitFor Without condition right?

                        1 Reply Last reply
                        0

                        7/11

                        23 Oct 2019, 18:22

                        • Login

                        • Login or register to search.
                        7 out of 11
                        • First post
                          7/11
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • Users
                        • Groups
                        • Search
                        • Get Qt Extensions
                        • Unsolved