Accessibility on macOS: make treeview test more reliable

Add a check that the reference we create the TestAXObject object from is
valid, and assert that it is before calling properties. Verify that the
rows we get all have the correct number of children (the columns) before
trying to read the cell values.

As a drive-by, change the relevant code to use property syntax, and
release objects consistently.

Task-number: QTBUG-122751
Change-Id: I1db3e4446c569235cfc0f3092adc7369ad826368
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
(cherry picked from commit dac1a338d31be82004f0f2666105ad18e557409b)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Volker Hilsheimer 2024-08-20 13:12:33 +02:00 committed by Qt Cherry-pick Bot
parent 8633139e25
commit 39524fabc4

View File

@ -70,6 +70,7 @@ QDebug operator<<(QDebug dbg, AXErrorTag err)
@property (readonly) NSString *value; @property (readonly) NSString *value;
@property (readonly) CGRect rect; @property (readonly) CGRect rect;
@property (readonly) NSArray *actions; @property (readonly) NSArray *actions;
@property (readonly) bool valid;
@end @end
@implementation TestAXObject @implementation TestAXObject
@ -78,8 +79,13 @@ QDebug operator<<(QDebug dbg, AXErrorTag err)
if ((self = [super init])) { if ((self = [super init])) {
reference = ref; reference = ref;
if (!reference) {
qCritical() << "Null reference!";
axError = true;
} else {
axError = false; axError = false;
} }
}
return self; return self;
} }
@ -344,6 +350,7 @@ QDebug operator<<(QDebug dbg, AXErrorTag err)
} }
} }
- (bool) valid { return reference != nil; }
- (NSString*) role { return [self _stringAttributeValue:kAXRoleAttribute]; } - (NSString*) role { return [self _stringAttributeValue:kAXRoleAttribute]; }
- (NSString*) title { return [self _stringAttributeValue:kAXTitleAttribute]; } - (NSString*) title { return [self _stringAttributeValue:kAXTitleAttribute]; }
- (NSString*) description { return [self _stringAttributeValue:kAXDescriptionAttribute]; } - (NSString*) description { return [self _stringAttributeValue:kAXDescriptionAttribute]; }
@ -771,47 +778,75 @@ void tst_QAccessibilityMac::treeViewTest()
AXUIElementRef windowRef = (AXUIElementRef)[windowList objectAtIndex:0]; AXUIElementRef windowRef = (AXUIElementRef)[windowList objectAtIndex:0];
QVERIFY(windowRef != nil); QVERIFY(windowRef != nil);
TestAXObject *window = [[TestAXObject alloc] initWithAXUIElementRef:windowRef]; TestAXObject *window = [[TestAXObject alloc] initWithAXUIElementRef:windowRef];
QVERIFY(window.valid);
// children of window // children of window
AXUIElementRef treeView = [window findDirectChildByRole:kAXOutlineRole]; AXUIElementRef treeView = [window findDirectChildByRole:kAXOutlineRole];
QVERIFY(treeView != nil); QVERIFY(treeView != nil);
TestAXObject *tv = [[TestAXObject alloc] initWithAXUIElementRef:treeView]; TestAXObject *tv = [[TestAXObject alloc] initWithAXUIElementRef:treeView];
QVERIFY(tv.valid);
[appObject release];
[window release];
// here start actual treeview tests. NSAccessibilityOutline is a specialization // here start actual treeview tests. NSAccessibilityOutline is a specialization
// of NSAccessibilityTable, and we represent trees as tables. // of NSAccessibilityTable, and we represent trees as tables.
const auto cellText = [tv](int rowIndex, int columnIndex) -> QString {
NSArray *rowArray = [tv tableRows];
Q_ASSERT(rowArray.count > uint(rowIndex));
TestAXObject *row = [[TestAXObject alloc] initWithAXUIElementRef:(AXUIElementRef)rowArray[rowIndex]];
Q_ASSERT(row && row.valid);
TestAXObject *cell = [[TestAXObject alloc] initWithAXUIElementRef:(AXUIElementRef)row.childList[columnIndex]];
Q_ASSERT(cell && cell.valid);
QString result = QString::fromNSString(cell.title);
[rowArray release];
return result;
};
// Should have 2 columns // Should have 2 columns
const unsigned int columnCount = 2; const unsigned int columnCount = 2;
NSArray *columnArray = [tv tableColumns]; {
QCOMPARE([columnArray count], columnCount); NSArray *columnArray = tv.tableColumns;
QCOMPARE(columnArray.count, columnCount);
[columnArray release];
}
// should have 1 row for now - as long as the root item is not expanded // Should have 1 row for now - as long as the root item is not expanded.
NSArray *rowArray = [tv tableRows]; // That row should have 2 columns.
QCOMPARE(int([rowArray count]), 1); {
NSArray *rowArray = tv.tableRows;
QCOMPARE(rowArray.count, 1u);
{
TestAXObject *row = [[TestAXObject alloc] initWithAXUIElementRef:(AXUIElementRef)rowArray[0]];
QCOMPARE(row.childList.count, columnCount);
[row release];
}
[rowArray release];
}
QCOMPARE(cellText(0, 0), root->text(0));
root->setExpanded(true); root->setExpanded(true);
rowArray = [tv tableRows]; // Now that the root node is expanded we should now see all rows.
QCOMPARE(int([rowArray count]), root->childCount() + 1); // Each row should have the same number of columns as the tree view.
{
NSArray *rowArray = tv.tableRows;
QCOMPARE(rowArray.count, root->childCount() + 1u);
for (ulong r = 0; r < rowArray.count; ++r) {
QVERIFY(rowArray[r] != nil);
TestAXObject *row = [[TestAXObject alloc] initWithAXUIElementRef:(AXUIElementRef)rowArray[r]];
const uint childCount = row.childList.count;
QCOMPARE(childCount, columnCount);
[row release];
}
[rowArray release];
}
// this should not trigger any assert // this should not trigger any assert
tw->setCurrentItem(lastChild); tw->setCurrentItem(lastChild);
bool errorOccurred = false; QCOMPARE(cellText(0, 0), root->text(0));
const auto cellText = [rowArray, &errorOccurred](int rowIndex, int columnIndex) -> QString {
TestAXObject *row = [[TestAXObject alloc] initWithAXUIElementRef:(AXUIElementRef)rowArray[rowIndex]];
Q_ASSERT(row);
TestAXObject *cell = [[TestAXObject alloc] initWithAXUIElementRef:(AXUIElementRef)[row childList][columnIndex]];
Q_ASSERT(cell);
const QString result = QString::fromNSString(cell.title);
errorOccurred = cell.errorOccurred;
return result;
};
QString text = cellText(0, 0);
if (errorOccurred)
QSKIP("Cocoa Accessibility API error, aborting");
QCOMPARE(text, root->text(0));
QCOMPARE(cellText(1, 0), users->text(0)); QCOMPARE(cellText(1, 0), users->text(0));
QCOMPARE(cellText(1, 1), users->text(1)); QCOMPARE(cellText(1, 1), users->text(1));
} }