e5fcad3 by Lars Knoll at 2009-03-23 1
/****************************************************************************
2
**
89c08c0 by Jason McDonald at 2012-01-11 3
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
04e3b30 by Jason McDonald at 2009-09-09 4
** All rights reserved.
858c70f by Jason McDonald at 2009-06-16 5
** Contact: Nokia Corporation (qt-info@nokia.com)
e5fcad3 by Lars Knoll at 2009-03-23 6
**
7
** This file is part of the test suite of the Qt Toolkit.
8
**
9
** $QT_BEGIN_LICENSE:LGPL$
10
** GNU Lesser General Public License Usage
1eea52e by Jyri Tahtela at 2011-05-13 11
** This file may be used under the terms of the GNU Lesser General Public
12
** License version 2.1 as published by the Free Software Foundation and
13
** appearing in the file LICENSE.LGPL included in the packaging of this
14
** file. Please review the following information to ensure the GNU Lesser
15
** General Public License version 2.1 requirements will be met:
16
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
e5fcad3 by Lars Knoll at 2009-03-23 17
**
04e3b30 by Jason McDonald at 2009-09-09 18
** In addition, as a special exception, Nokia gives you certain additional
1eea52e by Jyri Tahtela at 2011-05-13 19
** rights. These rights are described in the Nokia Qt LGPL Exception
04e3b30 by Jason McDonald at 2009-09-09 20
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
e5fcad3 by Lars Knoll at 2009-03-23 21
**
1eea52e by Jyri Tahtela at 2011-05-13 22
** GNU General Public License Usage
23
** Alternatively, this file may be used under the terms of the GNU General
24
** Public License version 3.0 as published by the Free Software Foundation
25
** and appearing in the file LICENSE.GPL included in the packaging of this
26
** file. Please review the following information to ensure the GNU General
27
** Public License version 3.0 requirements will be met:
28
** http://www.gnu.org/copyleft/gpl.html.
29
**
30
** Other Usage
31
** Alternatively, this file may be used in accordance with the terms and
32
** conditions contained in a signed written agreement between you and Nokia.
309db73 by Jason McDonald at 2009-08-31 33
**
34
**
35
**
36
**
e5fcad3 by Lars Knoll at 2009-03-23 37
**
38
** $QT_END_LICENSE$
39
**
40
****************************************************************************/
41
42
43
#include "guitest.h"
44
#include <QDebug>
45
#include <QWidget>
46
#include <QStack>
47
#include <QTimer>
48
#include <QtTest/QtTest>
49
50
#ifdef Q_OS_MAC
51
#   include <private/qt_mac_p.h>
52
#endif
53
54
55
/*
56
    Not really a test, just prints interface info.
57
*/
58
class PrintTest : public TestBase
59
{
60
public:
61
    bool operator()(InterfaceChildPair candidate) 
62
    {
63
        qDebug() << "";
64
        qDebug() << "Name" << candidate.iface->text(QAccessible::Name, candidate.possibleChild);
65
        qDebug() << "Pos" <<  candidate.iface->rect(candidate.possibleChild);
66
        if (candidate.possibleChild == 0)
67
            qDebug() << "Number of children" << candidate.iface->childCount();
68
        return false;
69
    }
70
};
71
72
class NameTest : public TestBase
73
{
74
public:
75
    NameTest(const QString &text, QAccessible::Text textType) : text(text), textType(textType) {}
76
    QString text;
77
    QAccessible::Text textType;
78
79
    bool operator()(InterfaceChildPair candidate) 
80
    {
81
        return (candidate.iface->text(textType, candidate.possibleChild) == text);
82
    }
83
};
84
85
void WidgetNavigator::printAll(QWidget *widget)
86
{
87
    QAccessibleInterface * const iface = QAccessible::queryAccessibleInterface(widget);
88
    deleteInDestructor(iface);
89
    printAll(InterfaceChildPair(iface, 0));
90
}
91
92
void WidgetNavigator::printAll(InterfaceChildPair interface) 
93
{
94
    PrintTest printTest;
95
    recursiveSearch(&printTest, interface.iface, interface.possibleChild);
96
}
97
98
InterfaceChildPair WidgetNavigator::find(QAccessible::Text textType, const QString &text, QWidget *start)
99
{
100
    QAccessibleInterface * const iface = QAccessible::queryAccessibleInterface(start);
101
    deleteInDestructor(iface);
102
    return find(textType, text, iface);
103
}
104
105
InterfaceChildPair WidgetNavigator::find(QAccessible::Text textType, const QString &text, QAccessibleInterface *start)
106
{
107
    NameTest nameTest(text, textType);
108
    return recursiveSearch(&nameTest, start, 0);
109
}
110
111
/*
112
    Recursiveley navigates the accessible hiearchy looking for an interfafce that
113
    passsed the Test (meaning it returns true).
114
*/
115
InterfaceChildPair WidgetNavigator::recursiveSearch(TestBase *test, QAccessibleInterface *iface, int possibleChild)
116
{
117
    QStack<InterfaceChildPair> todoInterfaces;
118
    todoInterfaces.push(InterfaceChildPair(iface, possibleChild));
119
120
    while (todoInterfaces.isEmpty() == false) {
121
        InterfaceChildPair testInterface = todoInterfaces.pop();
122
        
123
        if ((*test)(testInterface))
124
            return testInterface;
125
            
126
        if (testInterface.possibleChild != 0)
127
            continue;
128
129
        const int numChildren = testInterface.iface->childCount();
130
        for (int i = 0; i < numChildren; ++i) {
131
            QAccessibleInterface *childInterface = 0;
132
            int newPossibleChild = testInterface.iface->navigate(QAccessible::Child, i + 1, &childInterface);
133
            if (childInterface) {
134
                todoInterfaces.push(InterfaceChildPair(childInterface, newPossibleChild));
135
                deleteInDestructor(childInterface);
136
            } else if (newPossibleChild != -1) {
137
                todoInterfaces.push(InterfaceChildPair(testInterface.iface, newPossibleChild));
138
            }
139
        }
140
    }
141
    return InterfaceChildPair();
142
}
143
144
void WidgetNavigator::deleteInDestructor(QAccessibleInterface * interface)
145
{
146
    interfaces.insert(interface);
147
}
148
149
QWidget *WidgetNavigator::getWidget(InterfaceChildPair interface)
150
{
151
    return qobject_cast<QWidget *>(interface.iface->object());
152
}
153
154
WidgetNavigator::~WidgetNavigator()
155
{
156
    foreach(QAccessibleInterface *interface, interfaces) {
157
        delete interface;
158
    }
159
}
160
161
///////////////////////////////////////////////////////////////////////////////
162
163
namespace NativeEvents {
164
#ifdef Q_OS_MAC
165
   void mouseClick(const QPoint &globalPos, Qt::MouseButtons buttons, MousePosition updateMouse)
166
    {
167
        CGPoint position;
168
        position.x = globalPos.x();
169
        position.y = globalPos.y();
170
       
171
        const bool updateMousePosition = (updateMouse == UpdatePosition);
172
        
173
        // Mouse down.
174
        CGPostMouseEvent(position, updateMousePosition, 3, 
175
                        (buttons & Qt::LeftButton) ? true : false, 
176
                        (buttons & Qt::MidButton/* Middlebutton! */) ? true : false, 
177
                        (buttons & Qt::RightButton) ? true : false);
178
179
        // Mouse up.
180
        CGPostMouseEvent(position, updateMousePosition, 3, false, false, false);	
181
    }
182
#else
183
# error Oops, NativeEvents::mouseClick() is not implemented on this platform.
184
#endif
185
};
186
187
///////////////////////////////////////////////////////////////////////////////
188
189
GuiTester::GuiTester()
190
{
191
    clearSequence();
192
}
193
194
GuiTester::~GuiTester()
195
{
196
    foreach(DelayedAction *action, actions)
197
        delete action;
198
}
199
200
bool checkPixel(QColor pixel, QColor expected)
201
{
202
    const int allowedDiff = 20;
203
204
    return !(qAbs(pixel.red() - expected.red()) > allowedDiff ||
205
            qAbs(pixel.green() - expected.green()) > allowedDiff ||
206
            qAbs(pixel.blue() - expected.blue()) > allowedDiff);
207
}
208
209
/*
210
    Tests that the pixels inside rect in image all have the given color. 
211
*/
212
bool GuiTester::isFilled(const QImage image, const QRect &rect, const QColor &color)
213
{
214
    for (int y = rect.top(); y <= rect.bottom(); ++y)
215
        for (int x = rect.left(); x <= rect.right(); ++x) {
216
            const QColor pixel = image.pixel(x, y);
217
            if (checkPixel(pixel, color) == false) {
218
//                qDebug()<< "Wrong pixel value at" << x << y << pixel.red() << pixel.green() << pixel.blue();
219
                return false;
220
            }
221
        }
222
    return true;
223
}
224
225
226
/*
227
    Tests that stuff is painted to the pixels inside rect.
228
    This test fails if any lines in the given direction have pixels 
229
    of only one color.
230
*/
231
bool GuiTester::isContent(const QImage image, const QRect &rect, Directions directions)
232
{
233
    if (directions & Horizontal) {
234
        for (int y = rect.top(); y <= rect.bottom(); ++y) {
235
            QColor currentColor = image.pixel(rect.left(), y);
236
            bool fullRun = true;
237
            for (int x = rect.left() + 1; x <= rect.right(); ++x) {
238
                if (checkPixel(image.pixel(x, y), currentColor) == false) {
239
                    fullRun = false;
240
                    break;
241
                }
242
            }
243
            if (fullRun) {
244
//                qDebug() << "Single-color line at horizontal line " << y  << currentColor;
245
                return false;
246
            }
247
        }
248
        return true;
249
    } 
250
251
    if (directions & Vertical) {
252
       for (int x = rect.left(); x <= rect.right(); ++x) {
253
            QRgb currentColor = image.pixel(x, rect.top());
254
            bool fullRun = true;
255
            for (int y = rect.top() + 1; y <= rect.bottom(); ++y) {
256
                if (checkPixel(image.pixel(x, y), currentColor) == false) {
257
                    fullRun = false;
258
                    break;
259
                }
260
            }
261
            if (fullRun) {
262
//                qDebug() << "Single-color line at vertical line" << x << currentColor;
263
                return false;
264
            }
265
        }
266
        return true;
267
    }
268
    return false; // shut the compiler up.
269
}
270
271
void DelayedAction::run()
272
{
273
    if (next)
274
        QTimer::singleShot(next->delay, next, SLOT(run()));
275
};
276
277
/*
278
    Schedules a mouse click at an interface using a singleShot timer.
279
    Only one click can be scheduled at a time.
280
*/
281
ClickLaterAction::ClickLaterAction(InterfaceChildPair interface, Qt::MouseButtons buttons)
282
{
283
    this->useInterface = true;
284
    this->interface = interface;
285
    this->buttons = buttons;
286
}
287
288
/*
289
    Schedules a mouse click at a widget using a singleShot timer.
290
    Only one click can be scheduled at a time.
291
*/
292
ClickLaterAction::ClickLaterAction(QWidget *widget, Qt::MouseButtons buttons)
293
{
294
    this->useInterface = false;
295
    this->widget  = widget;
296
    this->buttons = buttons;
297
}
298
299
void ClickLaterAction::run()
300
{
301
    if (useInterface) {
302
        const QPoint globalCenter = interface.iface->rect(interface.possibleChild).center();
303
        NativeEvents::mouseClick(globalCenter, buttons);
304
    } else { // use widget
305
        const QSize halfSize = widget->size() / 2;
306
        const QPoint globalCenter = widget->mapToGlobal(QPoint(halfSize.width(), halfSize.height()));
307
        NativeEvents::mouseClick(globalCenter, buttons);
308
    }
309
    DelayedAction::run();
310
}
311
312
void GuiTester::clickLater(InterfaceChildPair interface, Qt::MouseButtons buttons, int delay)
313
{
314
    clearSequence();
315
    addToSequence(new ClickLaterAction(interface, buttons), delay);
316
    runSequence();
317
}
318
319
void GuiTester::clickLater(QWidget *widget, Qt::MouseButtons buttons, int delay)
320
{
321
    clearSequence();
322
    addToSequence(new ClickLaterAction(widget, buttons), delay);
323
    runSequence();
324
}
325
326
void GuiTester::clearSequence()
327
{
328
    startAction = new DelayedAction();
329
    actions.insert(startAction);
330
    lastAction = startAction;
331
}
332
333
void GuiTester::addToSequence(DelayedAction *action, int delay)
334
{
335
    actions.insert(action);
336
    action->delay = delay;
337
    lastAction->next = action;
338
    lastAction = action;
339
}
340
341
void GuiTester::runSequence()
342
{
343
    QTimer::singleShot(0, startAction, SLOT(run()));
344
}
345
346
void GuiTester::exitLoopSlot()
347
{
348
    QTestEventLoop::instance().exitLoop();
349
}
350