diff options
Diffstat (limited to 'tests/auto/qaccessibility_mac')
-rw-r--r-- | tests/auto/qaccessibility_mac/.gitignore | 1 | ||||
-rw-r--r-- | tests/auto/qaccessibility_mac/buttons.ui | 83 | ||||
-rw-r--r-- | tests/auto/qaccessibility_mac/combobox.ui | 50 | ||||
-rw-r--r-- | tests/auto/qaccessibility_mac/form.ui | 22 | ||||
-rw-r--r-- | tests/auto/qaccessibility_mac/groups.ui | 100 | ||||
-rw-r--r-- | tests/auto/qaccessibility_mac/label.ui | 35 | ||||
-rw-r--r-- | tests/auto/qaccessibility_mac/lineedit.ui | 35 | ||||
-rw-r--r-- | tests/auto/qaccessibility_mac/listview.ui | 89 | ||||
-rw-r--r-- | tests/auto/qaccessibility_mac/qaccessibility_mac.pro | 24 | ||||
-rw-r--r-- | tests/auto/qaccessibility_mac/qaccessibility_mac.qrc | 15 | ||||
-rw-r--r-- | tests/auto/qaccessibility_mac/radiobutton.ui | 38 | ||||
-rw-r--r-- | tests/auto/qaccessibility_mac/scrollbar.ui | 38 | ||||
-rw-r--r-- | tests/auto/qaccessibility_mac/splitters.ui | 52 | ||||
-rw-r--r-- | tests/auto/qaccessibility_mac/tableview.ui | 114 | ||||
-rw-r--r-- | tests/auto/qaccessibility_mac/tabs.ui | 68 | ||||
-rw-r--r-- | tests/auto/qaccessibility_mac/textBrowser.ui | 40 | ||||
-rw-r--r-- | tests/auto/qaccessibility_mac/tst_qaccessibility_mac.cpp | 1960 |
17 files changed, 2764 insertions, 0 deletions
diff --git a/tests/auto/qaccessibility_mac/.gitignore b/tests/auto/qaccessibility_mac/.gitignore new file mode 100644 index 0000000..54daa4f --- /dev/null +++ b/tests/auto/qaccessibility_mac/.gitignore @@ -0,0 +1 @@ +tst_qaccessibility_mac diff --git a/tests/auto/qaccessibility_mac/buttons.ui b/tests/auto/qaccessibility_mac/buttons.ui new file mode 100644 index 0000000..d2857c4 --- /dev/null +++ b/tests/auto/qaccessibility_mac/buttons.ui @@ -0,0 +1,83 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>Form</class> + <widget class="QWidget" name="Form" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>320</width> + <height>277</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <widget class="QPushButton" name="pushButton" > + <property name="geometry" > + <rect> + <x>30</x> + <y>40</y> + <width>109</width> + <height>32</height> + </rect> + </property> + <property name="text" > + <string>Ren</string> + </property> + </widget> + <widget class="QToolButton" name="toolButton" > + <property name="geometry" > + <rect> + <x>180</x> + <y>110</y> + <width>71</width> + <height>41</height> + </rect> + </property> + <property name="text" > + <string>Brain</string> + </property> + </widget> + <widget class="QRadioButton" name="radioButton" > + <property name="geometry" > + <rect> + <x>180</x> + <y>50</y> + <width>104</width> + <height>20</height> + </rect> + </property> + <property name="text" > + <string>Stimpy</string> + </property> + <property name="checked" > + <bool>true</bool> + </property> + </widget> + <widget class="QCheckBox" name="checkBox" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="geometry" > + <rect> + <x>40</x> + <y>120</y> + <width>90</width> + <height>20</height> + </rect> + </property> + <property name="text" > + <string>Pinky</string> + </property> + <property name="tristate" > + <bool>false</bool> + </property> + </widget> + </widget> + <pixmapfunction></pixmapfunction> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/qaccessibility_mac/combobox.ui b/tests/auto/qaccessibility_mac/combobox.ui new file mode 100644 index 0000000..abe3f07 --- /dev/null +++ b/tests/auto/qaccessibility_mac/combobox.ui @@ -0,0 +1,50 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>Form</class> + <widget class="QWidget" name="Form" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <widget class="QComboBox" name="comboBox1" > + <property name="geometry" > + <rect> + <x>30</x> + <y>30</y> + <width>171</width> + <height>26</height> + </rect> + </property> + <property name="currentIndex" > + <number>0</number> + </property> + <item> + <property name="text" > + <string>Foo</string> + </property> + </item> + <item> + <property name="text" > + <string>Bar</string> + </property> + </item> + <item> + <property name="text" > + <string>Baz</string> + </property> + </item> + </widget> + </widget> + <pixmapfunction></pixmapfunction> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/qaccessibility_mac/form.ui b/tests/auto/qaccessibility_mac/form.ui new file mode 100644 index 0000000..0ef5560 --- /dev/null +++ b/tests/auto/qaccessibility_mac/form.ui @@ -0,0 +1,22 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>Form</class> + <widget class="QWidget" name="Form" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>481</width> + <height>402</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + </widget> + <pixmapfunction></pixmapfunction> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/qaccessibility_mac/groups.ui b/tests/auto/qaccessibility_mac/groups.ui new file mode 100644 index 0000000..0bf6b13 --- /dev/null +++ b/tests/auto/qaccessibility_mac/groups.ui @@ -0,0 +1,100 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>Form</class> + <widget class="QWidget" name="Form" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>529</width> + <height>408</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <widget class="QGroupBox" name="groupBox" > + <property name="geometry" > + <rect> + <x>60</x> + <y>30</y> + <width>181</width> + <height>161</height> + </rect> + </property> + <property name="title" > + <string>Group A</string> + </property> + <widget class="QPushButton" name="pushButton" > + <property name="geometry" > + <rect> + <x>0</x> + <y>30</y> + <width>171</width> + <height>32</height> + </rect> + </property> + <property name="text" > + <string>PushButton 1</string> + </property> + </widget> + <widget class="QPushButton" name="pushButton_2" > + <property name="geometry" > + <rect> + <x>20</x> + <y>80</y> + <width>131</width> + <height>32</height> + </rect> + </property> + <property name="text" > + <string>PushButton 2</string> + </property> + </widget> + </widget> + <widget class="QGroupBox" name="groupBox_2" > + <property name="geometry" > + <rect> + <x>300</x> + <y>30</y> + <width>181</width> + <height>161</height> + </rect> + </property> + <property name="title" > + <string>Group B</string> + </property> + <widget class="QPushButton" name="pushButton_3" > + <property name="geometry" > + <rect> + <x>10</x> + <y>30</y> + <width>171</width> + <height>32</height> + </rect> + </property> + <property name="text" > + <string>PushButton 3</string> + </property> + </widget> + <widget class="QPushButton" name="pushButton_2_2" > + <property name="geometry" > + <rect> + <x>20</x> + <y>80</y> + <width>131</width> + <height>32</height> + </rect> + </property> + <property name="text" > + <string>PushButton 4</string> + </property> + </widget> + </widget> + </widget> + <pixmapfunction></pixmapfunction> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/qaccessibility_mac/label.ui b/tests/auto/qaccessibility_mac/label.ui new file mode 100644 index 0000000..8d86b42 --- /dev/null +++ b/tests/auto/qaccessibility_mac/label.ui @@ -0,0 +1,35 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>Form</class> + <widget class="QWidget" name="Form" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <widget class="QLabel" name="label" > + <property name="geometry" > + <rect> + <x>80</x> + <y>50</y> + <width>231</width> + <height>16</height> + </rect> + </property> + <property name="text" > + <string>This is a Text Label</string> + </property> + </widget> + </widget> + <pixmapfunction></pixmapfunction> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/qaccessibility_mac/lineedit.ui b/tests/auto/qaccessibility_mac/lineedit.ui new file mode 100644 index 0000000..479f7d3 --- /dev/null +++ b/tests/auto/qaccessibility_mac/lineedit.ui @@ -0,0 +1,35 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>Form</class> + <widget class="QWidget" name="Form" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <widget class="QLineEdit" name="lineEdit" > + <property name="geometry" > + <rect> + <x>70</x> + <y>70</y> + <width>113</width> + <height>20</height> + </rect> + </property> + <property name="text" > + <string>Line edit</string> + </property> + </widget> + </widget> + <pixmapfunction></pixmapfunction> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/qaccessibility_mac/listview.ui b/tests/auto/qaccessibility_mac/listview.ui new file mode 100644 index 0000000..f7d570e --- /dev/null +++ b/tests/auto/qaccessibility_mac/listview.ui @@ -0,0 +1,89 @@ +<ui version="4.0" > + <class>Form</class> + <widget class="QWidget" name="Form" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout" > + <item> + <widget class="QListWidget" name="listWidget" > + <item> + <property name="text" > + <string>Item 1</string> + </property> + </item> + <item> + <property name="text" > + <string>Item 2</string> + </property> + </item> + <item> + <property name="text" > + <string>Item 3</string> + </property> + </item> + <item> + <property name="text" > + <string>Item 4</string> + </property> + </item> + <item> + <property name="text" > + <string>New Item</string> + </property> + </item> + <item> + <property name="text" > + <string>New Item</string> + </property> + </item> + <item> + <property name="text" > + <string>New Item</string> + </property> + </item> + <item> + <property name="text" > + <string>New Item</string> + </property> + </item> + <item> + <property name="text" > + <string>New Item</string> + </property> + </item> + <item> + <property name="text" > + <string>New Item</string> + </property> + </item> + <item> + <property name="text" > + <string>New Item</string> + </property> + </item> + <item> + <property name="text" > + <string>New Item</string> + </property> + </item> + <item> + <property name="text" > + <string>New Item</string> + </property> + </item> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/qaccessibility_mac/qaccessibility_mac.pro b/tests/auto/qaccessibility_mac/qaccessibility_mac.pro new file mode 100644 index 0000000..b6829dc --- /dev/null +++ b/tests/auto/qaccessibility_mac/qaccessibility_mac.pro @@ -0,0 +1,24 @@ +CONFIG += qttest_p4 +SOURCES += tst_qaccessibility_mac.cpp +TARGET = tst_qaccessibility_mac + +RESOURCES = qaccessibility_mac.qrc + +mac { +# this setup should support both building as an autotest +# (where uilib isn't built by default), and when running shadow +# builds (where QTDIR points to the build directory). +# autotest + shadow build is not supported :) + exists($$(QTDIR)/tools/designer/src/lib/uilib/uilib.pri) { + include($$(QTDIR)/tools/designer/src/lib/uilib/uilib.pri) + INCLUDEPATH += $$(QTDIR)/tools/designer/src/uitools + SOURCES += $$(QTDIR)/tools/designer/src/uitools/quiloader.cpp + HEADERS += $$(QTDIR)/tools/designer/src/uitools/quiloader.h + } else { + CONFIG += uitools + } + QT += xml + LIBS += -framework ApplicationServices -framework Carbon +} + + diff --git a/tests/auto/qaccessibility_mac/qaccessibility_mac.qrc b/tests/auto/qaccessibility_mac/qaccessibility_mac.qrc new file mode 100644 index 0000000..511a4fb --- /dev/null +++ b/tests/auto/qaccessibility_mac/qaccessibility_mac.qrc @@ -0,0 +1,15 @@ + <!DOCTYPE RCC><RCC version="1.0"> + <qresource> + <file>buttons.ui</file> + <file>form.ui</file> + <file>label.ui</file> + <file>radiobutton.ui</file> + <file>splitters.ui</file> + <file>combobox.ui</file> + <file>groups.ui</file> + <file>lineedit.ui</file> + <file>scrollbar.ui</file> + <file>tabs.ui</file> + </qresource> + </RCC> + diff --git a/tests/auto/qaccessibility_mac/radiobutton.ui b/tests/auto/qaccessibility_mac/radiobutton.ui new file mode 100644 index 0000000..d09b3c8 --- /dev/null +++ b/tests/auto/qaccessibility_mac/radiobutton.ui @@ -0,0 +1,38 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>Form</class> + <widget class="QWidget" name="Form" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>426</width> + <height>327</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <widget class="QRadioButton" name="radioButton" > + <property name="geometry" > + <rect> + <x>60</x> + <y>40</y> + <width>221</width> + <height>20</height> + </rect> + </property> + <property name="text" > + <string>A checked radio button</string> + </property> + <property name="checked" > + <bool>true</bool> + </property> + </widget> + </widget> + <pixmapfunction></pixmapfunction> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/qaccessibility_mac/scrollbar.ui b/tests/auto/qaccessibility_mac/scrollbar.ui new file mode 100644 index 0000000..2d12cf6 --- /dev/null +++ b/tests/auto/qaccessibility_mac/scrollbar.ui @@ -0,0 +1,38 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>Form</class> + <widget class="QWidget" name="Form" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <widget class="QScrollBar" name="horizontalScrollBar" > + <property name="geometry" > + <rect> + <x>20</x> + <y>20</y> + <width>16</width> + <height>300</height> + </rect> + </property> + <property name="value" > + <number>50</number> + </property> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + </widget> + </widget> + <pixmapfunction></pixmapfunction> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/qaccessibility_mac/splitters.ui b/tests/auto/qaccessibility_mac/splitters.ui new file mode 100644 index 0000000..8b341e4 --- /dev/null +++ b/tests/auto/qaccessibility_mac/splitters.ui @@ -0,0 +1,52 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class></class> + <widget class="QWidget" name="Form" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QSplitter" name="" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <widget class="QLabel" name="label" > + <property name="text" > + <string>Foo</string> + </property> + </widget> + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Bar</string> + </property> + </widget> + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>Baz</string> + </property> + </widget> + </widget> + </item> + </layout> + </widget> + <pixmapfunction></pixmapfunction> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/qaccessibility_mac/tableview.ui b/tests/auto/qaccessibility_mac/tableview.ui new file mode 100644 index 0000000..6f17f3e --- /dev/null +++ b/tests/auto/qaccessibility_mac/tableview.ui @@ -0,0 +1,114 @@ +<ui version="4.0" > + <class>Form</class> + <widget class="QWidget" name="Form" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>436</width> + <height>419</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout" > + <item> + <widget class="QTableWidget" name="tableWidget" > + <row> + <property name="text" > + <string>Row 1</string> + </property> + </row> + <row> + <property name="text" > + <string>row 2</string> + </property> + </row> + <row> + <property name="text" > + <string>row 3</string> + </property> + </row> + <row> + <property name="text" > + <string>row 4</string> + </property> + </row> + <row> + <property name="text" > + <string>New Row</string> + </property> + </row> + <row> + <property name="text" > + <string>New Row</string> + </property> + </row> + <row> + <property name="text" > + <string>New Row</string> + </property> + </row> + <row> + <property name="text" > + <string>New Row</string> + </property> + </row> + <row> + <property name="text" > + <string>New Row</string> + </property> + </row> + <row> + <property name="text" > + <string>New Row</string> + </property> + </row> + <row> + <property name="text" > + <string>New Row</string> + </property> + </row> + <row> + <property name="text" > + <string>New Row</string> + </property> + </row> + <column> + <property name="text" > + <string>Col A</string> + </property> + </column> + <column> + <property name="text" > + <string>Col B</string> + </property> + </column> + <item row="0" column="0" > + <property name="text" > + <string>Cell 1A</string> + </property> + </item> + <item row="0" column="1" > + <property name="text" > + <string>Cell 1B</string> + </property> + </item> + <item row="1" column="0" > + <property name="text" > + <string>Cell 2A</string> + </property> + </item> + <item row="1" column="1" > + <property name="text" > + <string>Cell 2B</string> + </property> + </item> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/qaccessibility_mac/tabs.ui b/tests/auto/qaccessibility_mac/tabs.ui new file mode 100644 index 0000000..2d42f13 --- /dev/null +++ b/tests/auto/qaccessibility_mac/tabs.ui @@ -0,0 +1,68 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>Form</class> + <widget class="QWidget" name="Form" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>643</width> + <height>496</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <widget class="QTabWidget" name="tabWidget" > + <property name="geometry" > + <rect> + <x>80</x> + <y>30</y> + <width>221</width> + <height>191</height> + </rect> + </property> + <widget class="QWidget" name="tab" > + <attribute name="title" > + <string>Tab 1</string> + </attribute> + <widget class="QPushButton" name="pushButton" > + <property name="geometry" > + <rect> + <x>30</x> + <y>50</y> + <width>109</width> + <height>32</height> + </rect> + </property> + <property name="text" > + <string>PushButton one</string> + </property> + </widget> + </widget> + <widget class="QWidget" name="tab" > + <attribute name="title" > + <string>Tab 2</string> + </attribute> + <widget class="QPushButton" name="pushButton_2" > + <property name="geometry" > + <rect> + <x>50</x> + <y>20</y> + <width>109</width> + <height>32</height> + </rect> + </property> + <property name="text" > + <string>PushButton two</string> + </property> + </widget> + </widget> + </widget> + </widget> + <pixmapfunction></pixmapfunction> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/qaccessibility_mac/textBrowser.ui b/tests/auto/qaccessibility_mac/textBrowser.ui new file mode 100644 index 0000000..41f4450 --- /dev/null +++ b/tests/auto/qaccessibility_mac/textBrowser.ui @@ -0,0 +1,40 @@ +<ui version="4.0" > + <class>Form</class> + <widget class="QWidget" name="Form" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <widget class="QTextBrowser" name="textBrowser" > + <property name="geometry" > + <rect> + <x>10</x> + <y>20</y> + <width>311</width> + <height>251</height> + </rect> + </property> + <property name="html" > + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Lucida Grande'; font-size:13pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Line one</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Line two</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Line three</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Line four</p></body></html></string> + </property> + </widget> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tests/auto/qaccessibility_mac/tst_qaccessibility_mac.cpp b/tests/auto/qaccessibility_mac/tst_qaccessibility_mac.cpp new file mode 100644 index 0000000..b0b8c8b --- /dev/null +++ b/tests/auto/qaccessibility_mac/tst_qaccessibility_mac.cpp @@ -0,0 +1,1960 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtTest/QtTest> + +#if defined(Q_WS_MAC) && !defined (QT_MAC_USE_COCOA) + +#include <private/qt_mac_p.h> +#undef verify // yes, lets reserve the word "verify" + +#include <QApplication> +#include <QDebug> +#include <QTimer> +#include <QString> +#include <QFile> +#include <QVariant> +#include <QPushButton> +#include <QToolBar> +#include <QSlider> +#include <QListWidget> +#include <QTableWidget> +#include <QScrollArea> +#include <QLabel> +#include <QScrollBar> +#include <QTextEdit> +#include <QAccessibleInterface> +#include <QAccessible> +#include <QPluginLoader> +#include <private/qaccessible_mac_p.h> +#include <quiloader.h> + +#include <sys/types.h> // for getpid() +#include <unistd.h> + +Q_DECLARE_METATYPE(AXUIElementRef); + +typedef QCFType<CFArrayRef> QCFArrayRef; + +class tst_accessibility_mac : public QObject +{ +Q_OBJECT +public slots: + void printInfo(); + void testForm(); + void testButtons(); + void testLineEdit(); + void testLabel(); + void testGroups(); + void testTabWidget(); + void testTabBar(); + void testComboBox(); + void testDeleteWidget(); + void testDeleteWidgets(); + void testMultipleWindows(); + void testHiddenWidgets(); + void testActions(); + void testChangeState(); + void testSlider(); + void testScrollArea(); + void testListView(); + void testTableView(); + void testScrollBar(); + void testSplitter(); + void testTextEdit(); + void testItemViewsWithoutModel(); +private slots: + void testQAElement(); + void testQAInterface(); + + // ui tests load an .ui file. + void uitests_data(); + void uitests(); + + void tests_data(); + void tests(); +private: + void runTest(const QString &testSlot); +}; + +/* + VERIFYs that there is no error and prints an error message if there is. +*/ +void testError(AXError error, const QString &text) +{ + if (error) + qDebug() << "Error" << error << text; + QVERIFY(error == 0); +} + +/* + Prints an CFArray holding CFStrings. +*/ +void printCFStringArray(CFArrayRef array, const QString &title) +{ + const int numElements = CFArrayGetCount(array); + qDebug() << "num" << title << " " << numElements; + + for (int i = 0; i < numElements; ++i) { + CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(array, i); + qDebug() << QCFString::toQString(str); + } +} + +QStringList toQStringList(const CFArrayRef array) +{ + const int numElements = CFArrayGetCount(array); + QStringList qtStrings; + + for (int i = 0; i < numElements; ++i) { + CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(array, i); + qtStrings.append(QCFString::toQString(str)); + } + + return qtStrings; +} + +QVariant AXValueToQVariant(AXValueRef value) +{ + QVariant var; + const AXValueType type = AXValueGetType(value); + switch (type) { + case kAXValueCGPointType : { + CGPoint point; + if (AXValueGetValue(value, type, &point)) + var = QPointF(point.x, point.y); + } break; + case kAXValueCGSizeType : { + CGSize size; + if (AXValueGetValue(value, type, &size)) + var = QSizeF(size.width, size.height); + } break; + case kAXValueCGRectType : { + CGRect rect; + if (AXValueGetValue(value, type, &rect)) + var = QRectF(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); + } break; + case kAXValueCFRangeType : +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) + case kAXValueAXErrorType : +#endif + case kAXValueIllegalType : + default: + qDebug() << "Illegal/Unsuported AXValue:" << type; + break; + }; + return var; +} + +/* + Converts a CFTypeRef to a QVariant, for certain selected types. Prints + an error message and returns QVariant() if the type is not supported. +*/ +QVariant CFTypeToQVariant(CFTypeRef value) +{ + QVariant var; + if (value == 0) + return var; + const uint typeID = CFGetTypeID(value); + if (typeID == CFStringGetTypeID()) { + var.setValue(QCFString::toQString((CFStringRef)value)); + } else if (typeID == CFBooleanGetTypeID()) { + var.setValue((bool)CFBooleanGetValue((CFBooleanRef)value)); + } else if (typeID == AXUIElementGetTypeID()) { + var.setValue((AXUIElementRef)value); + } else if (typeID == AXValueGetTypeID()) { + var = AXValueToQVariant((AXValueRef)value); + } else if (typeID == CFNumberGetTypeID()) { + CFNumberRef number = (CFNumberRef)value; + if (CFNumberGetType(number) != kCFNumberSInt32Type) + qDebug() << "unsupported number type" << CFNumberGetType(number); + int theNumber; + CFNumberGetValue(number, kCFNumberSInt32Type, &theNumber); + var.setValue(theNumber); + } else if (typeID == CFArrayGetTypeID()) { + CFArrayRef cfarray = static_cast<CFArrayRef>(value); + QVariantList list; + CFIndex size = CFArrayGetCount(cfarray); + for (CFIndex i = 0; i < size; ++i) + list << CFTypeToQVariant(CFArrayGetValueAtIndex(cfarray, i)); + var.setValue(list); + } else { + QCFString str = CFCopyTypeIDDescription(typeID); + qDebug() << "Unknown CFType: " << typeID << (QString)str; + } + return var; +} + +/* + Tests if a given attribute is supported by an element. Expects either + no error or error -25205 (Not supported). Causes a test failure + on other error values. +*/ +bool supportsAttribute(AXUIElementRef element, CFStringRef attribute) +{ + CFArrayRef array; + AXError err = AXUIElementCopyAttributeNames(element, &array); + if (err) { + testError(err, QLatin1String("unexpected error when testing for supported attribute") + QCFString::toQString(attribute)); + return false; + } + CFRange range; + range.location = 0; + range.length = CFArrayGetCount(array); + return CFArrayContainsValue(array, range, attribute); +} + +/* + Returns the accessibility attribute specified with attribute in a QVariant +*/ +QVariant attribute(AXUIElementRef element, CFStringRef attribute) +{ + CFTypeRef value = 0; + AXError err = AXUIElementCopyAttributeValue(element, attribute, &value); + + testError(err, QString("Error getting element attribute ") + QCFString::toQString(attribute)); + + if (err) + return QVariant(); + + return CFTypeToQVariant(value); +} + +/* + Returns the title for an element. +*/ +QString title(AXUIElementRef element) +{ + return attribute(element, kAXTitleAttribute).toString(); +} + +/* + Returns the role for an element. +*/ +QString role(AXUIElementRef element) +{ + return attribute(element, kAXRoleAttribute).toString(); +} + +/* + Returns the subrole for an element. +*/ +QString subrole(AXUIElementRef element) +{ + return attribute(element, kAXSubroleAttribute).toString(); +} + +/* + Returns the role description for an element. +*/ +QString roleDescription(AXUIElementRef element) +{ + return attribute(element, kAXRoleDescriptionAttribute).toString(); +} + +/* + Returns the enabled attribute for an element. +*/ +bool enabled(AXUIElementRef element) +{ + return attribute(element, kAXEnabledAttribute).toBool(); +} + +/* + Returns the value attribute for an element as an QVariant. +*/ +QVariant value(AXUIElementRef element) +{ + return attribute(element, kAXValueAttribute); +} + +QVariant value(QAElement element) +{ + return value(element.element()); +} + +/* + Returns the description attribute for an element as an QVariant. +*/ +QVariant description(AXUIElementRef element) +{ + return attribute(element, kAXDescriptionAttribute); +} + +/* + Returns the value attribute for an element as an bool. +*/ +bool boolValue(AXUIElementRef element) +{ + return attribute(element, kAXValueAttribute).toBool(); +} + +/* + Returns the parent for an element +*/ +AXUIElementRef parent(AXUIElementRef element) +{ + return attribute(element, kAXParentAttribute).value<AXUIElementRef>(); +} + +/* + Returns the (top-level) window(not a sheet or a drawer) for an element +*/ +AXUIElementRef window(AXUIElementRef element) +{ + return attribute(element, kAXWindowAttribute).value<AXUIElementRef>(); +} + +/* + Returns the (top-level) UI element(can also be a sheet or drawer) for an element +*/ +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) +AXUIElementRef topLevelUIElement(AXUIElementRef element) +{ + return attribute(element, kAXTopLevelUIElementAttribute).value<AXUIElementRef>(); +} +#endif + +/* + Returns thie size of the element. +*/ +QSizeF size(AXUIElementRef element) +{ + return attribute(element, kAXSizeAttribute).value<QSizeF>(); +} + +/* + Returns the position of the element. +*/ +QPointF position(AXUIElementRef element) +{ + return attribute(element, kAXPositionAttribute).value<QPointF>(); +} + +/* + Returns the rect of the element. +*/ +QRectF rect(AXUIElementRef element) +{ + return QRectF(position(element), size(element)); +} + +bool above(AXUIElementRef a, AXUIElementRef b) +{ + return (position(a).y() + size(a).height() <= position(b).y()); +} + +bool contains(AXUIElementRef a, AXUIElementRef b) +{ + return rect(a).contains(rect(b)); +} + +QList<AXUIElementRef> tabs(AXUIElementRef element) +{ + CFTypeRef value; + AXError err = AXUIElementCopyAttributeValue(element, kAXTabsAttribute, &value); + if (err) + return QList<AXUIElementRef>(); + + CFArrayRef array = (CFArrayRef)value; + QList<AXUIElementRef> elements; + const int count = CFArrayGetCount(array); + for (int i = 0; i < count; ++i) + elements.append((AXUIElementRef)CFArrayGetValueAtIndex(array, i)); + + return elements; +} + +QList<AXUIElementRef> elementListAttribute(AXUIElementRef element, CFStringRef attributeName) +{ + QList<AXUIElementRef> elementList; + QVariantList variants = attribute(element, attributeName).value<QVariantList>(); + foreach(QVariant variant, variants) + elementList.append(variant.value<AXUIElementRef>()); + return elementList; +} + +AXUIElementRef elementAttribute(AXUIElementRef element, CFStringRef attributeName) +{ + return attribute(element, attributeName).value<AXUIElementRef>(); +} + +QString stringAttribute(AXUIElementRef element, CFStringRef attributeName) +{ + return attribute(element, attributeName).value<QString>(); +} + + +/* + Returns the UIElement at the given position. +*/ +AXUIElementRef childAtPoint(QPointF position) +{ + AXUIElementRef element = 0; + const AXError err = AXUIElementCopyElementAtPosition(AXUIElementCreateApplication(getpid()), position.x(), position.y(), &element); + if (err) { + qDebug() << "Error getting element at " << position; + return 0; + } + + return element; +} + +/* + Returns a QStringList containing the names of the actions the ui element supports +*/ +QStringList actionNames(AXUIElementRef element) +{ + CFArrayRef cfStrings; + const AXError err = AXUIElementCopyActionNames(element, &cfStrings); + testError(err, "Unable to get action names"); + return toQStringList(cfStrings); +} + +bool supportsAction(const AXUIElementRef element, const QString &actionName) +{ + const QStringList actions = actionNames(element); + return actions.contains(actionName); +} + +bool performAction(const AXUIElementRef element, const QString &actionName) +{ + const AXError err = AXUIElementPerformAction(element, QCFString(actionName)); + return (err == 0); +} + +/* + Om 10.4 and up, verifyes the AXRoleDescription attribute for an element, + on 10.3 and below this test always passes. + + The reason for this is that the HICopyAccessibilityRoleDescription call + used to implement this functionality was introduced in 10.4. +*/ +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) + #define VERIFY_ROLE_DESCRIPTION(ELEMENT, TEXT) \ + QCOMPARE(roleDescription(ELEMENT), QString(TEXT)) +#else + #define VERIFY_ROLE_DESCRIPTION(ELEMENT, TEXT) QVERIFY(true) +#endif + + +CFArrayRef childrenArray(AXUIElementRef element) +{ + CFTypeRef value = 0; + AXError err = AXUIElementCopyAttributeValue(element, kAXChildrenAttribute, &value); + if (!err && CFGetTypeID(value) == CFArrayGetTypeID()) { + return (CFArrayRef)value; + } + + return CFArrayCreate(0,0,0,0); +} + +/* + Gest the child count from an element. +*/ +int numChildren(AXUIElementRef element) +{ + return CFArrayGetCount(childrenArray(element)); +} + +/* + Gets the child with index childIndex from element. Returns 0 if not found. +*/ +AXUIElementRef child(AXUIElementRef element, int childIndex) +{ + CFArrayRef children = childrenArray(element); + if (childIndex >= CFArrayGetCount(children)) + return 0; + + const void *data = CFArrayGetValueAtIndex(children, childIndex); + return (AXUIElementRef)data; +} + +/* + Gets the child titled childTitle from element. Returns 0 if not found. +*/ +AXUIElementRef childByTitle(AXUIElementRef element, const QString &childTitle) +{ + CFArrayRef children = childrenArray(element); + const int numChildren = CFArrayGetCount(children); + for (int i = 0; i < numChildren; ++i) { + const AXUIElementRef childElement = (AXUIElementRef)CFArrayGetValueAtIndex(children, i); + // Test for support for title attribute before getting it to avoid test fail. + if (supportsAttribute(childElement, kAXTitleAttribute) && title(childElement) == childTitle) + return childElement; + } + return 0; +} + +/* + Gets the child with the given value from element. Returns 0 if not found. +*/ +AXUIElementRef childByValue(AXUIElementRef element, const QVariant &testValue) +{ + CFArrayRef children = childrenArray(element); + const int numChildren = CFArrayGetCount(children); + for (int i = 0; i < numChildren; ++i) { + const AXUIElementRef childElement = (AXUIElementRef)CFArrayGetValueAtIndex(children, i); + // Test for support for value attribute before getting it to avoid test fail. + if (supportsAttribute(childElement, kAXValueAttribute) && value(childElement) == testValue) + return childElement; + } + return 0; +} + +/* + Gets the child by role from element. Returns 0 if not found. +*/ +AXUIElementRef childByRole(AXUIElementRef element, const QString &macRole) +{ + CFArrayRef children = childrenArray(element); + const int numChildren = CFArrayGetCount(children); + for (int i = 0; i < numChildren; ++i) { + const AXUIElementRef childElement = (AXUIElementRef)CFArrayGetValueAtIndex(children, i); + if (role(childElement) == macRole) + return childElement; + } + return 0; +} + +void printTypeForAttribute(AXUIElementRef element, CFStringRef attribute) +{ + CFTypeRef value = 0; + AXError err = AXUIElementCopyAttributeValue(element, attribute, &value); + if (!err) { + qDebug() << "type id" << CFGetTypeID(value); + QCFString str = CFCopyTypeIDDescription(CFGetTypeID(value)); + qDebug() << (QString)str; + } else { + qDebug() << "Attribute Get error" << endl; + } +} + +int indent = 0; +QString space() +{ + QString space; + for (int i = 0; i < indent; ++i) { + space += " "; + } + return space; +} + + +/* + Recursively prints acccesibility info for currentElement and all its children. +*/ +void printElementInfo(AXUIElementRef currentElement) +{ + if (HIObjectIsAccessibilityIgnored(AXUIElementGetHIObject(currentElement))) { + qDebug() << space() << "Ignoring element with role" << role(currentElement); + return; + } + + qDebug() << space() <<"Role" << role(currentElement); + if (supportsAttribute(currentElement, kAXTitleAttribute)) + qDebug() << space() << "Title" << title(currentElement); + else + qDebug() << space() << "Title not supported"; + + if (supportsAttribute(currentElement, kAXValueAttribute)) + qDebug() << space() << "Value" << attribute(currentElement, kAXValueAttribute); + else + qDebug() << space() << "Value not supported"; + + qDebug() << space() << "Number of children" << numChildren(currentElement); + for (int i = 0; i < numChildren(currentElement); ++i) { + AXUIElementRef childElement = child(currentElement, i); + // Skip the menu bar. + if (role(childElement) != "AXMenuBar") { + indent+= 4; + printElementInfo(childElement); + indent-= 4; + } + } + qDebug() << " "; +} + +/* + Recursively prints the child interfaces belonging to interface. +*/ + +void printChildren(const QAInterface &interface) +{ + if (interface.isValid() == false) + return; + + QList<QAInterface> children = interface.children(); + if (children.isEmpty()) + return; + + qDebug() << "## Children for" << interface; + foreach (const QAInterface &child, children) { + qDebug() << child << "index in parent" << interface.indexOfChild(child); + } + foreach (const QAInterface &child, children) { + printChildren(child); + } +} + +bool isIgnored(AXUIElementRef currentElement) +{ + return HIObjectIsAccessibilityIgnored(AXUIElementGetHIObject(currentElement)); +} + +bool equal(CFTypeRef o1, CFTypeRef o2) +{ + if (o1 == 0 || o2 == 0) + return false; + return CFEqual(o1, o2); +} + +/* + Verifies basic element info. +*/ +#define VERIFY_ELEMENT(element, _parent, _role) \ + QVERIFY(element != 0); \ + QVERIFY(role(element) == _role); \ + QVERIFY(equal(::parent(element), _parent)); +/* + Verifies that the application and the main form is there has the right info. +*/ +void testAppAndForm(AXUIElementRef application) +{ + QVERIFY(title(application) == "tst_qaccessibility_mac"); + QVERIFY(role(application) == "AXApplication"); + + AXUIElementRef form = childByTitle(application, "Form"); + VERIFY_ELEMENT(form, application, "AXWindow"); +} + +void tst_accessibility_mac::printInfo() +{ + AXUIElementRef currentApplication = AXUIElementCreateApplication(getpid()); + printElementInfo(currentApplication); +} + +/* + Tests for form.ui +*/ +void tst_accessibility_mac::testForm() +{ + // Get reference to the current application. + AXUIElementRef currentApplication = AXUIElementCreateApplication(getpid()); + testAppAndForm(currentApplication); + childByTitle(currentApplication, "Form"); +} + +/* + Tests for buttons.ui +*/ +void tst_accessibility_mac::testButtons() +{ + // Get reference to the current application. + AXUIElementRef currentApplication = AXUIElementCreateApplication(getpid()); + testAppAndForm(currentApplication); + AXUIElementRef form = childByTitle(currentApplication, "Form"); + + AXUIElementRef ren = childByTitle(form, "Ren"); + VERIFY_ELEMENT(ren, form, "AXButton"); + QVERIFY(enabled(ren) == true); + VERIFY_ROLE_DESCRIPTION(ren, "button"); + + AXUIElementRef stimpy = childByTitle(form, "Stimpy"); + VERIFY_ELEMENT(stimpy, form, "AXRadioButton"); + QVERIFY(enabled(stimpy) == true); + QVERIFY(value(stimpy).toInt() == 1); // checked; + VERIFY_ROLE_DESCRIPTION(stimpy, "radio button"); + + AXUIElementRef pinky = childByTitle(form, "Pinky"); + VERIFY_ELEMENT(pinky, form, "AXCheckBox"); + QVERIFY(enabled(pinky) == false); + QVERIFY(value(pinky).toInt() == 0); // unchecked; + VERIFY_ROLE_DESCRIPTION(pinky, "check box"); + + AXUIElementRef brain = childByTitle(form, "Brain"); + VERIFY_ELEMENT(brain, form, "AXButton"); + VERIFY_ROLE_DESCRIPTION(brain, "button"); +} + +void tst_accessibility_mac::testLabel() +{ + // Get reference to the current application. + AXUIElementRef currentApplication = AXUIElementCreateApplication(getpid()); + + testAppAndForm(currentApplication); + AXUIElementRef form = childByTitle(currentApplication, "Form"); + AXUIElementRef label = childByValue(form, "This is a Text Label"); + QVERIFY(label); + VERIFY_ELEMENT(label, form, "AXStaticText"); + VERIFY_ROLE_DESCRIPTION(label, "text"); + QCOMPARE(supportsAttribute(label, kAXDescriptionAttribute), false); +} + +/* + Tests for lineedit.ui +*/ +void tst_accessibility_mac::testLineEdit() +{ + // Get reference to the current application. + AXUIElementRef currentApplication = AXUIElementCreateApplication(getpid()); + + testAppAndForm(currentApplication); + AXUIElementRef form = childByTitle(currentApplication, "Form"); + AXUIElementRef lineEdit = childByValue(form, "Line edit"); + VERIFY_ELEMENT(lineEdit, form, "AXTextField"); + VERIFY_ROLE_DESCRIPTION(lineEdit, "text field"); +} + +/* + Tests for groups.ui +*/ +void tst_accessibility_mac::testGroups() +{ + // Get reference to the current application. + AXUIElementRef currentApplication = AXUIElementCreateApplication(getpid()); + + testAppAndForm(currentApplication); + AXUIElementRef form = childByTitle(currentApplication, "Form"); + + AXUIElementRef groupA = childByTitle(form, "Group A"); + VERIFY_ELEMENT(groupA, form, "AXGroup"); + AXUIElementRef button1 = childByTitle(groupA, "PushButton 1"); + VERIFY_ELEMENT(button1, groupA, "AXButton"); + VERIFY_ROLE_DESCRIPTION(groupA, "group"); + + AXUIElementRef groupB = childByTitle(form, "Group B"); + VERIFY_ELEMENT(groupB, form, "AXGroup"); + AXUIElementRef button3 = childByTitle(groupB, "PushButton 3"); + VERIFY_ELEMENT(button3, groupB, "AXButton"); +} + +/* + Tests for tabs.ui +*/ +void tst_accessibility_mac::testTabWidget() +{ + { // Test that the QTabWidget hierarchy is what we expect it to be. + QTabWidget tabWidget; + tabWidget.show(); + QAInterface interface = QAccessible::queryAccessibleInterface(&tabWidget); + tabWidget.addTab(new QPushButton("Foo"), "FooTab"); + tabWidget.addTab(new QPushButton("Bar"), "BarTab"); + QCOMPARE(interface.childCount(), 2); + const QList<QAInterface> children = interface.children(); + QVERIFY(children.at(0).object()->inherits("QStackedWidget")); + QVERIFY(children.at(1).object()->inherits("QTabBar")); + + const QList<QAInterface> tabBarChildren = children.at(1).children(); + QCOMPARE(tabBarChildren.count(), 4); + QCOMPARE(tabBarChildren.at(0).text(QAccessible::Name), QLatin1String("FooTab")); + QCOMPARE(tabBarChildren.at(1).text(QAccessible::Name), QLatin1String("BarTab")); + QCOMPARE(tabBarChildren.at(0).role(), QAccessible::PageTab); + QCOMPARE(tabBarChildren.at(1).role(), QAccessible::PageTab); + + // Check that the hierarchy manager is able to register the tab bar children. + QAccessibleHierarchyManager *manager = QAccessibleHierarchyManager::instance(); + QAInterface tabBarInterface = children.at(1); + QAElement tabBarElement = manager->registerInterface(tabBarInterface); + QCOMPARE(manager->lookup(tabBarElement).childCount(), 4); + manager->registerChildren(tabBarInterface); + QAElement tabButtonElement = manager->lookup(tabBarChildren.at(1)); + QAInterface tabButtonInterface = manager->lookup(tabButtonElement); + QCOMPARE(tabButtonInterface.text(QAccessible::Name), QLatin1String("BarTab")); + QVERIFY(isItInteresting(tabButtonInterface) == true); + } + + // Get reference to the current application. + AXUIElementRef currentApplication = AXUIElementCreateApplication(getpid()); + + testAppAndForm(currentApplication); + const QString formTitle = "Form"; + AXUIElementRef form = childByTitle(currentApplication, formTitle); + QVERIFY(form); + + const QString tabRole = "AXTabGroup"; + AXUIElementRef tabGroup = childByRole(form, tabRole); + QVERIFY(tabGroup); + + // Test that we have three child buttons (the tab buttons + plus the contents of the first tab) + const int numChildren = ::numChildren(tabGroup); + QCOMPARE(numChildren, 3); + + const QString tab1Title = "Tab 1"; + AXUIElementRef tabButton1 = childByTitle(tabGroup, tab1Title); + QVERIFY (tabButton1); + VERIFY_ELEMENT(tabButton1, tabGroup, "AXRadioButton"); + QCOMPARE(title(tabButton1), tab1Title); + + const QString tab2Title = "Tab 2"; + const AXUIElementRef tabButton2 = childByTitle(tabGroup, tab2Title); + QVERIFY(tabButton2); + VERIFY_ELEMENT(tabButton2, tabGroup, "AXRadioButton"); + QCOMPARE(title(tabButton2), tab2Title); + + // Test that the window and top-level-ui-elment is the form. + // Window is not reported properly on 10.5 + if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_5) { + QVERIFY(equal(window(tabGroup), form)); + QVERIFY(equal(window(tabButton1), form)); + } +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) + QVERIFY(equal(topLevelUIElement(tabGroup), form)); + QVERIFY(equal(topLevelUIElement(tabButton1), form)); +#endif + // Test the bounding rectangles for the tab group and buttons. + const QRectF groupRect(position(tabGroup), size(tabGroup)); + const QRectF tabButton1Rect(position(tabButton1), size(tabButton1)); + const QRectF tabButton2Rect(position(tabButton2), size(tabButton2)); + + QVERIFY(groupRect.isNull() == false); + QVERIFY(tabButton1Rect.isNull() == false); + QVERIFY(tabButton1Rect.isNull() == false); + + QVERIFY(groupRect.contains(tabButton1Rect)); + QVERIFY(groupRect.contains(tabButton2Rect)); + QVERIFY(tabButton2Rect.contains(tabButton1Rect) == false); + + // Test the childAtPoint event. + const AXUIElementRef childAtTab1Position = childAtPoint(position(tabButton1) + QPointF(5,5)); + QVERIFY(equal(childAtTab1Position, tabButton1)); + const AXUIElementRef childAtOtherPosition = childAtPoint(position(tabButton1) - QPointF(5,5)); + QVERIFY(equal(childAtOtherPosition, tabButton1) == false); + + // Test AXTabs attribute + QVERIFY(supportsAttribute(tabGroup, kAXTabsAttribute)); + QList<AXUIElementRef> tabElements = tabs(tabGroup); + QCOMPARE(tabElements.count(), 2); + QVERIFY(equal(tabElements.at(0), tabButton1)); + QVERIFY(equal(tabElements.at(1), tabButton2)); + + // Perform the press action on each child. + for (int i = 0; i < numChildren; ++i) { + const AXUIElementRef child = ::child(tabGroup, i); + QVERIFY(supportsAction(child, "AXPress")); + QVERIFY(performAction(child, "AXPress")); + } +} + +void tst_accessibility_mac::testTabBar() +{ + QTabBar tabBar; + tabBar.addTab("Tab A"); + tabBar.addTab("Tab B"); + tabBar.show(); + + AXUIElementRef currentApplication = AXUIElementCreateApplication(getpid()); + AXUIElementRef window = childByRole(currentApplication, "AXWindow"); + QVERIFY(window); + + const QString tabRole = "AXTabGroup"; + AXUIElementRef tabGroup = childByRole(window, tabRole); + QVERIFY(tabGroup); + + const int numChildren = ::numChildren(tabGroup); + QCOMPARE(numChildren, 2); + + const QString tab1Title = "Tab A"; + AXUIElementRef tabButton1 = childByTitle(tabGroup, tab1Title); + QVERIFY (tabButton1); + VERIFY_ELEMENT(tabButton1, tabGroup, "AXRadioButton"); + QCOMPARE(title(tabButton1), tab1Title); + + const QString tab2Title = "Tab B"; + const AXUIElementRef tabButton2 = childByTitle(tabGroup, tab2Title); + QVERIFY(tabButton2); + VERIFY_ELEMENT(tabButton2, tabGroup, "AXRadioButton"); + QCOMPARE(title(tabButton2), tab2Title); + + // Test the childAtPoint event. + const AXUIElementRef childAtTab1Position = childAtPoint(position(tabButton1) + QPointF(5,5)); + QVERIFY(equal(childAtTab1Position, tabButton1)); + const AXUIElementRef childAtOtherPosition = childAtPoint(position(tabButton1) - QPointF(5,5)); + QVERIFY(equal(childAtOtherPosition, tabButton1) == false); + + // Test AXTabs attribute + QVERIFY(supportsAttribute(tabGroup, kAXTabsAttribute)); + QList<AXUIElementRef> tabElements = tabs(tabGroup); + QCOMPARE(tabElements.count(), 2); + QVERIFY(equal(tabElements.at(0), tabButton1)); + QVERIFY(equal(tabElements.at(1), tabButton2)); + + // Perform the press action on each child. + for (int i = 0; i < numChildren; ++i) { + const AXUIElementRef child = ::child(tabGroup, i); + QVERIFY(supportsAction(child, "AXPress")); + QVERIFY(performAction(child, "AXPress")); + } +} + +void tst_accessibility_mac::testComboBox() +{ + // Get reference to the current application. + AXUIElementRef currentApplication = AXUIElementCreateApplication(getpid()); + + testAppAndForm(currentApplication); + const QString formTitle = "Form"; + AXUIElementRef form = childByTitle(currentApplication, formTitle); + + const QString comboBoxRole = "AXPopUpButton"; + AXUIElementRef comboBox = childByRole(form, comboBoxRole); + QVERIFY(comboBox != 0); + QVERIFY(supportsAction(comboBox, "AXPress")); + QVERIFY(performAction(comboBox, "AXPress")); +} + +void tst_accessibility_mac::testDeleteWidget() +{ + const QString buttonTitle = "Hi there"; + QWidget *form = new QWidget(0, Qt::Window); + form->setWindowTitle("Form"); + form->show(); + QPushButton *button = new QPushButton(buttonTitle, form); + button->show(); + + AXUIElementRef currentApplication = AXUIElementCreateApplication(getpid()); + testAppAndForm(currentApplication); + AXUIElementRef formElement = childByTitle(currentApplication, "Form"); + + AXUIElementRef buttonElement = childByTitle(formElement, buttonTitle); + QVERIFY(buttonElement); + + button->hide(); + delete button; + + buttonElement = childByTitle(formElement, buttonTitle); + QVERIFY(!buttonElement); + delete form; +} + +void tst_accessibility_mac::testDeleteWidgets() +{ + const QString buttonTitle = "Hi there"; + const int repeats = 10; + + for (int i = 0; i < repeats; ++i) { + + QWidget *form = new QWidget(0, Qt::Window); + form->setWindowTitle("Form"); + form->show(); + + QPushButton *button = new QPushButton(buttonTitle, form); + button->show(); + + AXUIElementRef currentApplication = AXUIElementCreateApplication(getpid()); + testAppAndForm(currentApplication); + AXUIElementRef formElement = childByTitle(currentApplication, "Form"); + AXUIElementRef buttonElement = childByTitle(formElement, buttonTitle); + QVERIFY(buttonElement); + delete form; + + { + AXUIElementRef currentApplication = AXUIElementCreateApplication(getpid()); + QVERIFY(currentApplication); + AXUIElementRef formElement = childByTitle(currentApplication, "Form"); + QVERIFY(!formElement); + } + } + + for (int i = 0; i < repeats; ++i) { + QWidget *form = new QWidget(0, Qt::Window); + form->setWindowTitle("Form"); + + + new QScrollBar(form); + form->show(); + + AXUIElementRef currentApplication = AXUIElementCreateApplication(getpid()); + testAppAndForm(currentApplication); + AXUIElementRef formElement = childByTitle(currentApplication, "Form"); + + const AXUIElementRef scrollBarElement = childByRole(formElement, "AXScrollBar"); + QVERIFY(scrollBarElement); + delete form; + + { + AXUIElementRef currentApplication = AXUIElementCreateApplication(getpid()); + QVERIFY(currentApplication); + AXUIElementRef formElement = childByTitle(currentApplication, "Form"); + QVERIFY(!formElement); + } + } + + for (int i = 0; i < repeats; ++i) { + QWidget *form = new QWidget(0, Qt::Window); + form->setWindowTitle("Form"); + + QListWidget *listWidget = new QListWidget(form); + listWidget->addItem("Foo"); + listWidget->addItem("Bar"); + form->show(); + + AXUIElementRef currentApplication = AXUIElementCreateApplication(getpid()); + testAppAndForm(currentApplication); + AXUIElementRef formElement = childByTitle(currentApplication, "Form"); + + const AXUIElementRef scrollAreaElement = childByRole(formElement, "AXScrollArea"); + QVERIFY(scrollAreaElement); + + const AXUIElementRef listElement = childByRole(scrollAreaElement, "AXList"); + QVERIFY(listElement); + delete form; + + { + AXUIElementRef currentApplication = AXUIElementCreateApplication(getpid()); + QVERIFY(currentApplication); + AXUIElementRef formElement = childByTitle(currentApplication, "Form"); + QVERIFY(!formElement); + } + } + +} + +void tst_accessibility_mac::testMultipleWindows() +{ + const QString formATitle("FormA"); + const QString formBTitle("FormB"); + + // Create a window + QWidget *formA = new QWidget(0, Qt::Window); + formA->setWindowTitle(formATitle); + formA->show(); + + // Test if we can access the window + AXUIElementRef currentApplication = AXUIElementCreateApplication(getpid()); + AXUIElementRef formAElement = childByTitle(currentApplication, formATitle); + QVERIFY(formAElement); + + // Create another window + QWidget *formB = new QWidget(0, Qt::Window); + formB->setWindowTitle(formBTitle); + formB->show(); + + // Test if we can access both windows + formAElement = childByTitle(currentApplication, formATitle); + QVERIFY(formAElement); + + AXUIElementRef formBElement = childByTitle(currentApplication, formBTitle); + QVERIFY(formBElement); + + delete formA; +} + +void tst_accessibility_mac::testHiddenWidgets() +{ + const QString windowTitle ="a widget"; + QWidget * const window = new QWidget(0); + window->setWindowTitle(windowTitle); + window->show(); + + const AXUIElementRef currentApplication = AXUIElementCreateApplication(getpid()); + const AXUIElementRef windowElement = childByTitle(currentApplication, windowTitle); + QVERIFY(windowElement); + QCOMPARE(isIgnored(windowElement), false); + + const QString buttonTitle = "a button"; + QPushButton * const button = new QPushButton(window); + button->setText(buttonTitle); + button->show(); + + const AXUIElementRef buttonElement = childByTitle(windowElement, buttonTitle); + QVERIFY(buttonElement); + QCOMPARE(isIgnored(buttonElement), false); + + const QString toolbarTitle = "a toolbar"; + QToolBar * const toolbar = new QToolBar(toolbarTitle, window); + toolbar->show(); + + const AXUIElementRef toolBarElement = childByTitle(windowElement, toolbarTitle); + QVERIFY(toolBarElement == 0); + + delete window; +}; + +void tst_accessibility_mac::testActions() +{ + // create a window with a push button + const QString windowTitle ="a widget"; + QWidget * const window = new QWidget(); + window->setWindowTitle(windowTitle); + window->show(); + + const AXUIElementRef currentApplication = AXUIElementCreateApplication(getpid()); + const AXUIElementRef windowElement = childByTitle(currentApplication, windowTitle); + QVERIFY(windowElement); + + const QString buttonTitle = "a button"; + QPushButton * const button = new QPushButton(window); + button->setText(buttonTitle); + button->show(); + + const AXUIElementRef buttonElement = childByTitle(windowElement, buttonTitle); + QVERIFY(buttonElement); + + // Verify that the button has the Press action. + const QStringList actions = actionNames(buttonElement); + const QString pressActionName("AXPress"); + QVERIFY(actions.contains(pressActionName)); + + // Press button and check the pressed signal + QSignalSpy pressed(button, SIGNAL(pressed())); + QVERIFY(performAction(buttonElement, pressActionName)); + QCOMPARE(pressed.count(), 1); + + pressed.clear(); + QVERIFY(performAction(buttonElement, QString("does not exist"))); + QCOMPARE(pressed.count(), 0); + + delete window; +}; + +void tst_accessibility_mac::testChangeState() +{ + const QString windowTitle ="a widget"; + QWidget * const window = new QWidget(); + window->setWindowTitle(windowTitle); + window->show(); + + const AXUIElementRef applicationElement = AXUIElementCreateApplication(getpid()); + const AXUIElementRef windowElement = childByTitle(applicationElement, windowTitle); + QVERIFY(windowElement); + const int otherChildren = numChildren(windowElement); + + const QString buttonTitle = "Button"; + QPushButton * const button = new QPushButton(buttonTitle, window); + button->setText(buttonTitle); + + // Test that show/hide adds/removes the button from the hierachy. + QVERIFY(childByTitle(windowElement, buttonTitle) == 0); + QCOMPARE(numChildren(windowElement), otherChildren); + button->show(); + QVERIFY(childByTitle(windowElement, buttonTitle) != 0); + QCOMPARE(numChildren(windowElement), otherChildren + 1); + button->hide(); + QVERIFY(childByTitle(windowElement, buttonTitle) == 0); + QCOMPARE(numChildren(windowElement), otherChildren); + button->show(); + QVERIFY(childByTitle(windowElement, buttonTitle) != 0); + QCOMPARE(numChildren(windowElement), otherChildren + 1); + + // Test that hiding and showing a widget also removes and adds all its children. + { + QWidget * const parent = new QWidget(window); + const int otherChildren = numChildren(windowElement); + + QPushButton * const child = new QPushButton(parent); + const QString childButtonTitle = "child button"; + child->setText(childButtonTitle); + + parent->show(); + QVERIFY(childByTitle(windowElement, childButtonTitle) != 0); + QCOMPARE(numChildren(windowElement), otherChildren + 1); + + parent->hide(); + QVERIFY(childByTitle(windowElement, childButtonTitle) == 0); + QCOMPARE(numChildren(windowElement), otherChildren ); + + parent->show(); + QVERIFY(childByTitle(windowElement, childButtonTitle) != 0); + QCOMPARE(numChildren(windowElement), otherChildren + 1); + + delete parent; + } + + // Test that the enabled attribute is updated after a call to setEnabled. + const AXUIElementRef buttonElement = childByTitle(windowElement, buttonTitle); + QVERIFY(enabled(buttonElement)); + button->setEnabled(false); + QVERIFY(enabled(buttonElement) == false); + button->setEnabled(true); + QVERIFY(enabled(buttonElement)); + + // Test that changing the title updates the accessibility information. + const QString buttonTitle2 = "Button 2"; + button->setText(buttonTitle2); + QVERIFY(childByTitle(windowElement, buttonTitle2) != 0); + QVERIFY(childByTitle(windowElement, buttonTitle) == 0); + + delete window; +} + +void tst_accessibility_mac::testSlider() +{ + const QString windowTitle = "a widget"; + QWidget * const window = new QWidget(); + window->setWindowTitle(windowTitle); + window->show(); + + const AXUIElementRef applicationElement = AXUIElementCreateApplication(getpid()); + QVERIFY(applicationElement); + const AXUIElementRef windowElement = childByTitle(applicationElement, windowTitle); + QVERIFY(windowElement); + const int windowChildren = numChildren(windowElement); + + QSlider * const slider = new QSlider(window); + slider->show(); + const AXUIElementRef sliderElement = childByRole(windowElement, "AXSlider"); + QVERIFY(sliderElement); + + // Test that the slider and its children are removed from the hierachy when we call hide(). + QCOMPARE(numChildren(windowElement), windowChildren + 1); + slider->hide(); + QCOMPARE(numChildren(windowElement), windowChildren); + + delete slider; +} + +void tst_accessibility_mac::testScrollArea() +{ + QWidget window; + const QString windowTitle = "window"; + window.setWindowTitle(windowTitle); + window.resize(300, 300); + + QScrollArea scrollArea(&window); + scrollArea.resize(300, 300); + scrollArea.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + + QLabel label; + label.setText("Foo"); + scrollArea.setWidget(&label); + + window.show(); + + // Verify that the QAinterface returns the correct children + QAInterface interface = QAccessible::queryAccessibleInterface(&scrollArea); + QCOMPARE(interface.childCount(), 3); + + QAInterface viewport = interface.navigate(QAccessible::Child, 1); + QVERIFY(viewport.isValid()); + + QAInterface scrollBarContainer1 = interface.navigate(QAccessible::Child, 2); + QVERIFY(scrollBarContainer1.isValid()); + + QAInterface scrollBar1 = scrollBarContainer1.navigate(QAccessible::Child, 1); + + QVERIFY(scrollBar1.isValid()); + QVERIFY(scrollBar1.role() == QAccessible::ScrollBar); + + QAInterface scrollBarContainer2 = interface.navigate(QAccessible::Child, 3); + QVERIFY(scrollBarContainer1.isValid()); + + QAInterface scrollBar2 = scrollBarContainer2.navigate(QAccessible::Child, 1); + QVERIFY(scrollBar2.isValid()); + QVERIFY(scrollBar2.role() == QAccessible::ScrollBar); + + // Navigate to the scroll area from the application + const AXUIElementRef applicationElement = AXUIElementCreateApplication(getpid()); + QVERIFY(applicationElement); + const AXUIElementRef windowElement = childByTitle(applicationElement, windowTitle); + QVERIFY(windowElement); + const AXUIElementRef scrollAreaElement = childByRole(windowElement, "AXScrollArea"); + QVERIFY(scrollAreaElement); + + // Get the scroll bars + QVERIFY(supportsAttribute(scrollAreaElement, kAXHorizontalScrollBarAttribute)); + const AXUIElementRef horizontalScrollBar = elementAttribute(scrollAreaElement, kAXHorizontalScrollBarAttribute); + QVERIFY(horizontalScrollBar); + QVERIFY(role(horizontalScrollBar) == "AXScrollBar"); + QVERIFY(stringAttribute(horizontalScrollBar, kAXOrientationAttribute) == "AXHorizontalOrientation"); + + QVERIFY(supportsAttribute(scrollAreaElement, kAXVerticalScrollBarAttribute)); + const AXUIElementRef verticalScrollBar = elementAttribute(scrollAreaElement, kAXVerticalScrollBarAttribute); + QVERIFY(verticalScrollBar); + QVERIFY(role(verticalScrollBar) == "AXScrollBar"); + QVERIFY(stringAttribute(verticalScrollBar, kAXOrientationAttribute) == "AXVerticalOrientation"); + + // Get the contents and verify that we get the label. + QVERIFY(supportsAttribute(scrollAreaElement, kAXContentsAttribute)); + const QList<AXUIElementRef> contents = elementListAttribute(scrollAreaElement, kAXContentsAttribute); + QCOMPARE(contents.count(), 1); + AXUIElementRef content = contents.at(0); + QVERIFY(role(content) == "AXStaticText"); + QVERIFY(title(content) == "Foo"); + + // Turn scroll bars off + { + scrollArea.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + QVERIFY(supportsAttribute(scrollAreaElement, kAXHorizontalScrollBarAttribute) == false); + QVERIFY(supportsAttribute(scrollAreaElement, kAXVerticalScrollBarAttribute) == false); + + QVERIFY(supportsAttribute(scrollAreaElement, kAXContentsAttribute)); + const QList<AXUIElementRef> contents = elementListAttribute(scrollAreaElement, kAXContentsAttribute); + QCOMPARE(contents.count(), 1); + AXUIElementRef content = contents.at(0); + + QVERIFY(role(content) == "AXStaticText"); + QVERIFY(title(content) == "Foo"); + } + + // Turn the horizontal scrollbar on. + { + scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + scrollArea.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + QVERIFY(supportsAttribute(scrollAreaElement, kAXHorizontalScrollBarAttribute) == true); + QVERIFY(supportsAttribute(scrollAreaElement, kAXVerticalScrollBarAttribute) == false); + + const AXUIElementRef horizontalScrollBar = elementAttribute(scrollAreaElement, kAXHorizontalScrollBarAttribute); + QVERIFY(horizontalScrollBar); + QVERIFY(role(horizontalScrollBar) == "AXScrollBar"); + QVERIFY(stringAttribute(horizontalScrollBar, kAXOrientationAttribute) == "AXHorizontalOrientation"); + + QVERIFY(supportsAttribute(scrollAreaElement, kAXContentsAttribute)); + const QList<AXUIElementRef> contents = elementListAttribute(scrollAreaElement, kAXContentsAttribute); + QCOMPARE(contents.count(), 1); + AXUIElementRef content = contents.at(0); + QVERIFY(role(content) == "AXStaticText"); + QVERIFY(title(content) == "Foo"); + } + + // Turn the vertical scrollbar on. + { + scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scrollArea.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + QVERIFY(supportsAttribute(scrollAreaElement, kAXHorizontalScrollBarAttribute) == false); + QVERIFY(supportsAttribute(scrollAreaElement, kAXVerticalScrollBarAttribute) == true); + + QVERIFY(supportsAttribute(scrollAreaElement, kAXVerticalScrollBarAttribute)); + const AXUIElementRef verticalScrollBar = elementAttribute(scrollAreaElement, kAXVerticalScrollBarAttribute); + QVERIFY(verticalScrollBar); + QVERIFY(role(verticalScrollBar) == "AXScrollBar"); + QVERIFY(stringAttribute(verticalScrollBar, kAXOrientationAttribute) == "AXVerticalOrientation"); + + QVERIFY(supportsAttribute(scrollAreaElement, kAXContentsAttribute)); + const QList<AXUIElementRef> contents = elementListAttribute(scrollAreaElement, kAXContentsAttribute); + QCOMPARE(contents.count(), 1); + AXUIElementRef content = contents.at(0); + QVERIFY(role(content) == "AXStaticText"); + QVERIFY(title(content) == "Foo"); + } +} + +void tst_accessibility_mac::testListView() +{ + QWidget window; + const QString windowTitle("window"); + window.setWindowTitle(windowTitle); + window.resize(300, 300); + + QListWidget *listWidget = new QListWidget(&window); + listWidget->setObjectName("listwidget"); + listWidget->addItem("A"); + listWidget->addItem("B"); + listWidget->addItem("C"); + + window.show(); + QTest::qWait(1); + + { + // Verify that QAInterface works as expected for list views + QAInterface listWidgetInterface = QAccessible::queryAccessibleInterface(listWidget); + QCOMPARE(listWidgetInterface.role(), QAccessible::Client); + QCOMPARE(listWidgetInterface.childCount(), 1); + QAInterface viewPort = listWidgetInterface.childAt(1); + QCOMPARE(viewPort.role(), QAccessible::List); + QVERIFY(viewPort.object() != 0); + QCOMPARE(viewPort.childCount(), 3); + const QList<QAInterface> rows = viewPort.children(); + QCOMPARE(rows.count(), 3); + QVERIFY(rows.at(0).object() == 0); + QCOMPARE(rows.at(0).parent().indexOfChild(rows.at(0)), 1); + QCOMPARE(rows.at(1).parent().indexOfChild(rows.at(1)), 2); + QCOMPARE(rows.at(2).parent().indexOfChild(rows.at(2)), 3); + + // test the QAInterface comparison operator + QVERIFY(rows.at(0) == rows.at(0)); + QVERIFY(rows.at(0) != rows.at(1)); + QVERIFY(rows.at(0) != viewPort); + QVERIFY(viewPort == viewPort); + QVERIFY(listWidgetInterface != viewPort); + QVERIFY(listWidgetInterface == listWidgetInterface); + + // test QAInterface::isHIView() + QVERIFY(viewPort.isHIView()); + QVERIFY(listWidgetInterface.isHIView()); + QVERIFY(rows.at(0).isHIView() == false); + } + + const AXUIElementRef applicationElement = AXUIElementCreateApplication(getpid()); + QVERIFY(applicationElement); + const AXUIElementRef windowElement = childByTitle(applicationElement, windowTitle); + QVERIFY(windowElement); + const AXUIElementRef scrollAreaElement = childByRole(windowElement, "AXScrollArea"); + QVERIFY(scrollAreaElement); + const AXUIElementRef listElement = childByRole(scrollAreaElement, "AXList"); + QVERIFY(listElement); + QVERIFY(equal(::parent(listElement), scrollAreaElement)); + + // Window is not reported properly on 10.5 + if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_5) + QVERIFY(equal(::window(listElement), windowElement)); + + const AXUIElementRef A = childByTitle(listElement, "A"); + QVERIFY(A); + const AXUIElementRef B = childByTitle(listElement, "B"); + QVERIFY(B); + const AXUIElementRef C = childByTitle(listElement, "C"); + QVERIFY(C); + + QVERIFY(value(A) == "A"); + QVERIFY(equal(::parent(A), listElement)); + QVERIFY(enabled(A)); + // Window is not reported properly on 10.5 + if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_5) + QVERIFY(equal(::window(A), windowElement)); + + QVERIFY(above(A, B)); + QVERIFY(!above(B, A)); + QVERIFY(above(B, C)); + QVERIFY(contains(listElement, A)); + QVERIFY(!contains(A, listElement)); + QVERIFY(contains(listElement, B)); + QVERIFY(contains(listElement, C)); +} + +void tst_accessibility_mac::testTableView() +{ + QWidget window; + const QString windowTitle("window"); + window.setWindowTitle(windowTitle); + window.resize(300, 300); + + QTableWidget *tableWidget = new QTableWidget(&window); + tableWidget->setObjectName("tablewidget"); + tableWidget->setRowCount(3); + tableWidget->setColumnCount(2); + + tableWidget->setItem(0, 0, new QTableWidgetItem("A1")); + tableWidget->setItem(0, 1, new QTableWidgetItem("A2")); + + tableWidget->setItem(1, 0, new QTableWidgetItem("B1")); + tableWidget->setItem(1, 1, new QTableWidgetItem("B2")); + + tableWidget->setItem(2, 0, new QTableWidgetItem("C1")); + tableWidget->setItem(2, 1, new QTableWidgetItem("C2")); + + window.show(); + + { + // Verify that QAInterface works as expected for table view children. + QAInterface tableWidgetInterface = QAccessible::queryAccessibleInterface(tableWidget); + QCOMPARE(tableWidgetInterface.role(), QAccessible::Client); + QCOMPARE(tableWidgetInterface.childCount(), 1); + QAInterface viewPort = tableWidgetInterface.childAt(1); + QCOMPARE(viewPort.childCount(), 4); + QCOMPARE(viewPort.role(), QAccessible::Table); + QVERIFY(viewPort.object() != 0); + const QList<QAInterface> rows = viewPort.children(); + QCOMPARE(rows.count(), 4); + QVERIFY(rows.at(0).object() == 0); + QCOMPARE(rows.at(0).parent().indexOfChild(rows.at(0)), 1); + QCOMPARE(rows.at(1).parent().indexOfChild(rows.at(1)), 2); + QCOMPARE(rows.at(2).parent().indexOfChild(rows.at(2)), 3); + + QAInterface Arow = rows.at(1); + QCOMPARE(Arow.role(), QAccessible::Row); + QAInterface Brow = rows.at(2); + + QVERIFY(Arow.name() == "1"); + QVERIFY(Brow.name() == "2"); + + QVERIFY(Arow == Arow); + QVERIFY(Brow != Arow); + QVERIFY(Arow.isHIView() == false); + QCOMPARE(Arow.childCount(), 3); + QList<QAInterface> Achildren = Arow.children(); + QCOMPARE(Achildren.count(), 3); + QAInterface A1 = Achildren.at(1); + QAInterface A2 = Achildren.at(2); + QCOMPARE(Arow.indexOfChild(A1), 2); + QCOMPARE(Arow.indexOfChild(A2), 3); + QCOMPARE(A1.role(), QAccessible::Cell); + + QList<QAInterface> Bchildren = Brow.children(); + QCOMPARE(Bchildren.count(), 3); + QAInterface B1 = Bchildren.at(1); + QAInterface B2 = Bchildren.at(2); + QVERIFY(B1.parent() == Brow); + QVERIFY(B1.parent() != Arow); + QCOMPARE(Arow.indexOfChild(B1), -1); + + QVERIFY(A1 == A1); + QVERIFY(A1 != A2); + QVERIFY(B1 != A1); + QVERIFY(B1 != A2); + QVERIFY(A1 != Arow); + QVERIFY(A1 != Brow); + QVERIFY(A1 != viewPort); + QVERIFY(A1.isHIView() == false); + + QVERIFY(B1.parent() == Brow); + QVERIFY(A1.parent() == Arow); + B1 = A1; + QVERIFY(B1.parent() == Arow); + } + + const AXUIElementRef applicationElement = AXUIElementCreateApplication(getpid()); + QVERIFY(applicationElement); + const AXUIElementRef windowElement = childByTitle(applicationElement, windowTitle); + QVERIFY(windowElement); + const AXUIElementRef scrollAreaElement = childByRole(windowElement, "AXScrollArea"); + QVERIFY(scrollAreaElement); + const AXUIElementRef tableElement = childByRole(scrollAreaElement, "AXTable"); + QVERIFY(tableElement); + + { + // Verify that QAccessibleHierarchyManager can look up table view children correctly + QAccessibleHierarchyManager *manager = QAccessibleHierarchyManager::instance(); + QAInterface tableInterface = manager->lookup(tableElement); + QVERIFY(tableInterface.isValid()); + QVERIFY(tableInterface.role() == QAccessible::Table); + + QAInterface ArowInterface = tableInterface.childAt(2); + + QVERIFY(ArowInterface.name() == "1"); + QVERIFY(manager->lookup(manager->lookup(ArowInterface)).name() == "1"); + + QCOMPARE(ArowInterface.childCount(), 3); + QAInterface A1Interface = ArowInterface.childAt(2); + + QCOMPARE(A1Interface.value(), QString("A1")); + QVERIFY(value(manager->lookup(A1Interface)).toString() == "A1"); + + QAInterface A2Interface = ArowInterface.childAt(3); + QAElement A2Element = manager->lookup(A2Interface); + QVERIFY(manager->lookup(A2Element).value() == "A2"); + QVERIFY(value(A2Element).toString() == "A2"); + + QAInterface BrowInterface = tableInterface.childAt(3); + QVERIFY(BrowInterface.value() == "2"); + QVERIFY(manager->lookup(manager->lookup(BrowInterface)).value() == "2"); + + QCOMPARE(BrowInterface.childCount(), 3); + QAInterface B1Interface = BrowInterface.childAt(2); + QVERIFY(value(manager->lookup(B1Interface)).toString() == "B1"); + + QAInterface B2Interface = BrowInterface.childAt(3); + QAElement B2Element = manager->lookup(B2Interface); + QVERIFY(manager->lookup(B2Element).value() == "B2"); + QVERIFY(value(B2Element).toString() == "B2"); + } + + + + const AXUIElementRef Arow = childByTitle(tableElement, "1"); + QVERIFY(Arow); + const AXUIElementRef Brow = childByTitle(tableElement, "2"); + QVERIFY(Brow); + const AXUIElementRef Crow = childByTitle(tableElement, "3"); + QVERIFY(Crow); + + QCOMPARE(numChildren(Arow), 3); + const AXUIElementRef A1cell = childByTitle(Arow, "A1"); + QVERIFY(A1cell); + QVERIFY(role(A1cell) == "AXTextField"); + const AXUIElementRef A2cell = childByTitle(Arow, "A2"); + QVERIFY(A2cell); + QVERIFY(equal(::parent(A2cell), Arow)); + + const AXUIElementRef B2cell = childByTitle(Brow, "B2"); + QVERIFY(B2cell); + QVERIFY(equal(::parent(B2cell), Brow)); + + { + QVERIFY(supportsAttribute(tableElement, kAXRowsAttribute)); + const QList<AXUIElementRef> rows = elementListAttribute(tableElement, kAXRowsAttribute); + QCOMPARE(rows.count(), 3); // the header is not a row + QVERIFY(value(rows.at(1)) == "2"); + QVERIFY(value(rows.at(2)) == "3"); + } + + { + QVERIFY(supportsAttribute(tableElement, kAXVisibleRowsAttribute)); + const QList<AXUIElementRef> rows = elementListAttribute(tableElement, kAXVisibleRowsAttribute); + QCOMPARE(rows.count(), 3); + QVERIFY(value(rows.at(1)) == "2"); + } + { + QVERIFY(supportsAttribute(tableElement, kAXSelectedRowsAttribute)); + const QList<AXUIElementRef> rows = elementListAttribute(tableElement, kAXSelectedRowsAttribute); + QCOMPARE(rows.count(), 0); + } + + // test row visibility + { + QTableWidget tableWidget; + tableWidget.setObjectName("tablewidget"); + tableWidget.setRowCount(1000); + tableWidget.setColumnCount(1); + + for (int i =0; i < 1000; ++i) { + tableWidget.setItem(i, 0, new QTableWidgetItem("item")); + } + tableWidget.show(); + + QAInterface tableWidgetInterface = QAccessible::queryAccessibleInterface(&tableWidget); + QAInterface viewPortInterface = tableWidgetInterface.childAt(1); + QCOMPARE(viewPortInterface.childCount(), 1001); + + QVERIFY((viewPortInterface.childAt(2).state() & QAccessible::Invisible) == false); + QVERIFY((viewPortInterface.childAt(2).state() & QAccessible::Offscreen) == false); + + QVERIFY(viewPortInterface.childAt(500).state() & QAccessible::Invisible); +// QVERIFY(viewPortInterface.childAt(500).state() & QAccessible::Offscreen); + tableWidget.hide(); + } + +// printElementInfo(tableElement); +// QTest::qWait(1000000); +} + +void tst_accessibility_mac::testScrollBar() +{ + { + QScrollBar scrollBar; + scrollBar.show(); + + QAInterface scrollBarInterface = QAccessible::queryAccessibleInterface(&scrollBar); + QVERIFY(scrollBarInterface.isValid()); + QCOMPARE(scrollBarInterface.childCount(), 5); + QCOMPARE(scrollBarInterface.indexOfChild(scrollBarInterface.childAt(1)), 1); + QCOMPARE(scrollBarInterface.indexOfChild(scrollBarInterface.childAt(2)), 2); + QCOMPARE(scrollBarInterface.indexOfChild(scrollBarInterface.childAt(5)), 5); + QCOMPARE(scrollBarInterface.indexOfChild(scrollBarInterface), -1); + } + + const AXUIElementRef currentApplication = AXUIElementCreateApplication(getpid()); + testAppAndForm(currentApplication); + const AXUIElementRef form = childByTitle(currentApplication, "Form"); + QVERIFY(form); + const AXUIElementRef scrollBarElement = childByRole(form, "AXScrollBar"); + QVERIFY(scrollBarElement); + QCOMPARE(attribute(scrollBarElement, kAXOrientationAttribute).toString(), QLatin1String("AXVerticalOrientation")); + + { + const AXUIElementRef lineUpElement = childByTitle(scrollBarElement, "Line up"); + QVERIFY(lineUpElement); + QCOMPARE (subrole(lineUpElement), QLatin1String("AXDecrementArrow")); + }{ + const AXUIElementRef lineDownElement = childByTitle(scrollBarElement, "Line down"); + QVERIFY(lineDownElement); + QCOMPARE (subrole(lineDownElement), QLatin1String("AXIncrementArrow")); + }{ + const AXUIElementRef pageUpElement = childByTitle(scrollBarElement, "Page up"); + QVERIFY(pageUpElement); + QCOMPARE (subrole(pageUpElement), QLatin1String("AXDecrementPage")); + }{ + const AXUIElementRef pageDownElement = childByTitle(scrollBarElement, "Page down"); + QVERIFY(pageDownElement); + QCOMPARE (subrole(pageDownElement), QLatin1String("AXIncrementPage")); + }{ + const AXUIElementRef valueIndicatorElement = childByTitle(scrollBarElement, "Position"); + QVERIFY(valueIndicatorElement); + QCOMPARE(value(valueIndicatorElement).toInt(), 50); + } +} + +void tst_accessibility_mac::testSplitter() +{ + const AXUIElementRef currentApplication = AXUIElementCreateApplication(getpid()); + testAppAndForm(currentApplication); + const AXUIElementRef form = childByTitle(currentApplication, "Form"); + QVERIFY(form); + + const AXUIElementRef splitGroupElement = childByRole(form, "AXSplitGroup"); + QVERIFY(splitGroupElement); + + for (int i = 0; i < numChildren(splitGroupElement); ++i) + QVERIFY(child(splitGroupElement, 3)); + + // Visual Order: Foo splitter Bar splitter Baz + QList<AXUIElementRef> splitterList = elementListAttribute(splitGroupElement, kAXSplittersAttribute); + QCOMPARE(splitterList.count(), 2); + foreach (AXUIElementRef splitter, splitterList) { + QCOMPARE(role(splitter), QLatin1String("AXSplitter")); + QVERIFY(supportsAttribute(splitter, kAXPreviousContentsAttribute)); + QVERIFY(supportsAttribute(splitter, kAXNextContentsAttribute)); + QCOMPARE(attribute(splitter, kAXOrientationAttribute).toString(), QLatin1String("AXVerticalOrientation")); + QList<AXUIElementRef> prevList = elementListAttribute(splitter, kAXPreviousContentsAttribute); + QCOMPARE(prevList.count(), 1); + QList<AXUIElementRef> nextList = elementListAttribute(splitter, kAXNextContentsAttribute); + QCOMPARE(nextList.count(), 1); + + // verify order + if (title(prevList.at(0)) == QLatin1String("Foo")) + QCOMPARE(title(nextList.at(0)), QLatin1String("Bar")); + else if (title(prevList.at(0)) == QLatin1String("Bar")) + QCOMPARE(title(nextList.at(0)), QLatin1String("Baz")); + else { + QFAIL("Splitter contents and handles are out of order"); + } + } +} + +void tst_accessibility_mac::testTextEdit() +{ + QWidget window; + const QString windowTitle("window"); + window.setWindowTitle(windowTitle); + window.resize(300, 300); + + QTextEdit *textEdit = new QTextEdit(&window); + textEdit->resize(300, 300); + const QString textLine("this is a line"); + textEdit->setText(textLine); + + window.show(); + + const AXUIElementRef applicationElement = AXUIElementCreateApplication(getpid()); + QVERIFY(applicationElement); + const AXUIElementRef windowElement = childByTitle(applicationElement, windowTitle); + QVERIFY(windowElement); + const AXUIElementRef scrollAreaElement = childByRole(windowElement, "AXScrollArea"); + QVERIFY(scrollAreaElement); + const AXUIElementRef textElement = childByRole(scrollAreaElement, "AXTextField"); + QVERIFY(textElement); + QVERIFY(value(textElement) == textLine); +} + +void testModelLessItemView(QAbstractItemView *itemView, const QByteArray &role) +{ + const QString windowTitle("window"); + itemView->setWindowTitle(windowTitle); + itemView->show(); + + QTest::qWait(100); +#if defined(Q_WS_X11) + qt_x11_wait_for_window_manager(w); +#endif + + QAccessibleInterface *acc = QAccessible::queryAccessibleInterface(itemView); + QVERIFY(acc->isValid()); + QCOMPARE(acc->childCount(), 1); + acc->role(0); + acc->rect(0); + + QAccessibleInterface *accViewport = 0; + int entry = acc->navigate(QAccessible::Child, 1, &accViewport); + QVERIFY(accViewport); + QCOMPARE(entry, 0); + QVERIFY(accViewport->isValid()); + QCOMPARE(accViewport->childCount(), 0); + accViewport->role(0); + accViewport->rect(0); + + delete acc; + delete accViewport; + + const AXUIElementRef applicationElement = AXUIElementCreateApplication(getpid()); + QVERIFY(applicationElement); + const AXUIElementRef windowElement = childByTitle(applicationElement, windowTitle); + QVERIFY(windowElement); + const AXUIElementRef scrollAreaElement = childByRole(windowElement, "AXScrollArea"); + QVERIFY(scrollAreaElement); + const AXUIElementRef tableElement = childByRole(scrollAreaElement, role); + QVERIFY(tableElement); + + delete itemView; +} + +void tst_accessibility_mac::testItemViewsWithoutModel() +{ + testModelLessItemView(new QListView(), "AXList"); + testModelLessItemView(new QTableView(), "AXTable"); +} + +void tst_accessibility_mac::testQAElement() +{ + { + QAElement element; + QVERIFY(element.isValid() == false); + } + + { + QAElement element(0, 0); + QVERIFY(element.isValid() == false); + } + + { + int argc = 0; + char **argv = 0; + QApplication app(argc, argv); + QWidget w; + QAElement element(reinterpret_cast<HIObjectRef>(w.winId()), 0); + QVERIFY(element.isValid() == true); + } + +} + +void tst_accessibility_mac::testQAInterface() +{ + { + QAInterface interface; + QVERIFY(interface.isValid() == false); + } + + { + QAInterface interface(0, 0); + QVERIFY(interface.isValid() == false); + } + + { + int argc = 0; + char **argv = 0; + QApplication app(argc, argv); + + { + QWidget w; + QAInterface element(QAccessible::queryAccessibleInterface(&w), 0); + QVERIFY(element.isValid() == true); + } + { + QWidget w; + QAInterface element(QAccessible::queryAccessibleInterface(&w), 100); + QVERIFY(element.isValid() == false); + } + } +} + +void tst_accessibility_mac::uitests_data() +{ + QTest::addColumn<QString>("uiFilename"); + QTest::addColumn<QString>("testSlot"); + + QTest::newRow("form") << "form.ui" << SLOT(testForm()); + QTest::newRow("buttons") << "buttons.ui" << SLOT(testButtons()); + QTest::newRow("label") << "label.ui" << SLOT(testLabel()); + QTest::newRow("line edit") << "lineedit.ui" << SLOT(testLineEdit()); + QTest::newRow("groups") << "groups.ui" << SLOT(testGroups()); + QTest::newRow("tabs") << "tabs.ui" << SLOT(testTabWidget()); + QTest::newRow("combobox") << "combobox.ui" << SLOT(testComboBox()); + QTest::newRow("scrollbar") << "scrollbar.ui" << SLOT(testScrollBar()); + QTest::newRow("splitters") << "splitters.ui" << SLOT(testSplitter()); +} + +void tst_accessibility_mac::uitests() +{ + QFETCH(QString, uiFilename); + QFETCH(QString, testSlot); + + // The Accessibility interface must be enabled to run this test. + if (!AXAPIEnabled()) + QSKIP("Accessibility not enabled. Check \"Enable access for assistive devices\" in the system preferences -> universal access to run this test.", SkipAll); + + int argc = 0; + char **argv = 0; + QApplication app(argc, argv); + + // Create and display form. + QUiLoader loader; + QFile file(":" + uiFilename); + QVERIFY(file.exists()); + file.open(QFile::ReadOnly); + QWidget *window = loader.load(&file, 0); + QVERIFY(window); + file.close(); + window->show(); + + QTimer::singleShot(50, this, qPrintable(testSlot)); + // Quit when returning to the main event loop after running tests. + QTimer::singleShot(200, &app, SLOT(quit())); + app.exec(); + delete window; +} + +void tst_accessibility_mac::tests_data() +{ + QTest::addColumn<QString>("testSlot"); + QTest::newRow("deleteWidget") << SLOT(testDeleteWidget()); + QTest::newRow("deleteWidgets") << SLOT(testDeleteWidgets()); + QTest::newRow("multipleWindows") << SLOT(testMultipleWindows()); + QTest::newRow("hiddenWidgets") << SLOT(testHiddenWidgets()); + QTest::newRow("actions") << SLOT(testActions()); + QTest::newRow("changeState") << SLOT(testChangeState()); + QTest::newRow("slider") << SLOT(testSlider()); + QTest::newRow("scrollArea") << SLOT(testScrollArea()); + QTest::newRow("listView") << SLOT(testListView()); + QTest::newRow("tableView") << SLOT(testTableView()); + QTest::newRow("textEdit") << SLOT(testTextEdit()); + QTest::newRow("ItemViews without model") << SLOT(testItemViewsWithoutModel()); + QTest::newRow("tabbar") << SLOT(testTabBar()); +} + +void tst_accessibility_mac::tests() +{ + QFETCH(QString, testSlot); + runTest(testSlot); +} + +/* + Tests show that querying the accessibility interface directly does not work. (I get a + kAXErrorAPIDisabled error, indicating that the accessible API is disabled, which it isn't.) + To work around this, we run the tests in a callback slot called from the main event loop. +*/ +void tst_accessibility_mac::runTest(const QString &testSlot) +{ + // The Accessibility interface must be enabled to run this test. + if (!AXAPIEnabled()) + QSKIP("Accessibility not enabled. Check \"Enable access for assistive devices\" in the system preferences -> universal access to run this test.", SkipAll); + + int argc = 0; + char **argv = 0; + QApplication app(argc, argv); + + QTimer::singleShot(50, this, qPrintable(testSlot)); + // Quit when returning to the main event loop after running tests. + QTimer::singleShot(200, &app, SLOT(quit())); + app.exec(); + +} + +QTEST_APPLESS_MAIN(tst_accessibility_mac) + +#else // defined(Q_WS_MAC) && !defined (QT_MAC_USE_COCOA) + +QTEST_NOOP_MAIN + +#endif + +#include "tst_qaccessibility_mac.moc" + + |