aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_node.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_node.py')
-rw-r--r--tests/test_node.py570
1 files changed, 570 insertions, 0 deletions
diff --git a/tests/test_node.py b/tests/test_node.py
new file mode 100644
index 00000000000..1353925e501
--- /dev/null
+++ b/tests/test_node.py
@@ -0,0 +1,570 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Unit tests for the dogtail.Node class
+
+Notes on pyunit (the "unittest" module):
+
+Test classes are written as subclass of unittest.TestCase.
+A test is a method of such a class, beginning with the string "test"
+
+unittest.main() will run all such methods. Use "-v" to get feedback on which tests are being run. Tests are run in alphabetical order; all failure reports are gathered at the end.
+
+setUp and tearDown are "magic" methods, called before and after each such
+test method is run.
+"""
+__author__ = "Dave Malcolm <dmalcolm@redhat.com>"
+
+import unittest
+import dogtail.tree
+import dogtail.predicate
+import dogtail.config
+dogtail.config.config.logDebugToFile = False
+import pyatspi
+from nose.tools import nottest
+from gtkdemotest import GtkDemoTest, trap_stdout
+
+
+class TestNodeAttributes(GtkDemoTest):
+
+ """
+ Unit tests for the the various synthesized attributes of a Node
+ """
+
+ def testGetBogus(self):
+ "Getting a non-existant attribute should raise an attribute error"
+ self.assertRaises(
+ AttributeError, getattr, self.app, "thisIsNotAnAttribute")
+
+ # FIXME: should setattr for a non-existant attr be allowed?
+
+ # 'name' (read-only string):
+ def testGetName(self):
+ """
+ Node.name of the gtk-demo app should be "gtk-demo"
+ """
+ self.assertEquals(self.app.name, 'gtk3-demo')
+
+ self.assertEquals(dogtail.tree.root.name, 'main')
+
+ def testSetName(self):
+ "Node.name should be read-only"
+ self.assertRaises(
+ AttributeError, self.app.__setattr__, "name", "hello world")
+
+ # 'roleName' (read-only string):
+ def testGetRoleName(self):
+ """
+ roleName of the gtk-demo app should be "application"
+ """
+ self.assertEquals(self.app.roleName, 'application')
+
+ def testSetRoleName(self):
+ """Node.roleName should be read-only"""
+ self.assertRaises(
+ AttributeError, self.app.__setattr__, "roleName", "hello world")
+
+ # 'role' (read-only atspi role enum):
+ def testGetRole(self):
+ """Node.role for a gtk-demo app should be SPI_ROLE_APPLICATION"""
+ self.assertEquals(self.app.role, dogtail.tree.pyatspi.ROLE_APPLICATION)
+
+ def testSetRole(self):
+ """Node.role should be read-only"""
+ # FIXME should be AttributeError?
+ self.assertRaises(
+ RuntimeError, self.app.__setattr__, "role", pyatspi.Atspi.Role(1))
+
+ # 'description' (read-only string):
+ def testGetDescription(self):
+ # FIXME: can we get a more interesting test case here?
+ self.assertEquals(self.app.description, "")
+
+ def testSetDescription(self):
+ "Node.description should be read-only"
+ self.assertRaises(
+ AttributeError, self.app.__setattr__, "description", "hello world")
+
+ # 'parent' (read-only Node instance):
+ def testGetParent(self):
+ # the app has a parent if gnome-shell is used, so parent.parent is a
+ # safe choice
+ if filter(lambda x: x.name == 'gnome-shell', self.app.applications()):
+ self.assertEquals(self.app.parent.parent, None)
+ self.assertEquals(self.app.children[0].parent, self.app)
+
+ def testSetParent(self):
+ "Node.parent should be read-only"
+ self.assertRaises(
+ AttributeError, self.app.__setattr__, "parent", None)
+
+ # 'children' (read-only list of Node instances):
+ def testGetChildren(self):
+ "A fresh gtk-demo app should have a single child: the window."
+ kids = self.app.children
+ self.assertEquals(len(kids), 1)
+ self.assertEquals(kids[0].name, "GTK+ Code Demos")
+ self.assertEquals(kids[0].roleName, "frame")
+
+ def testSetChildren(self):
+ "Node.children should be read-only"
+ self.assertRaises(
+ AttributeError, self.app.__setattr__, "children", [])
+
+ # 'text' (string):
+ @nottest
+ def testSimpleTextEntry(self):
+ """
+ Use gtk-demo's text entry example to check that reading and writing
+ Node.text works as expected
+ """
+ self.runDemo('Dialog and Message Boxes')
+ wnd = self.app.window('Dialogs')
+ wnd.button('Interactive Dialog').click()
+ dlg = self.app.dialog('Interactive Dialog')
+ entry1 = dlg.child(label='Entry 1')
+ entry2 = dlg.child(label='Entry 2')
+
+ # Try reading the entries:
+ self.assertEquals(entry1.text, "")
+ self.assertEquals(entry2.text, "")
+
+ # Set them...
+ entry1.text = "hello"
+ entry2.text = "world"
+
+ # Ensure that they got set:
+ self.assertEquals(entry1.text, "hello")
+ self.assertEquals(entry2.text, "world")
+
+ # and try again, searching for them again, to ensure it actually
+ # affected the UI:
+ self.assertEquals(dlg.child(label='Entry 1').text, "hello")
+ self.assertEquals(dlg.child(label='Entry 2').text, "world")
+
+ # Ensure app.text is None
+ self.assertEquals(self.app.text, None)
+
+ # Ensure a label's text is read-only as expected:
+ # FIXME: this doesn't work; the label has no 'text'; it has a name. we wan't a readonly text entry
+ # label = dlg.child('Entry 1')
+ # self.assertRaises(dogtail.tree.ReadOnlyError, label.text.__setattr__, "text", "hello world")
+
+ # FIXME: should we assert that things are logged and delays are added?
+ # FIXME: should have a test case involving the complex GtkTextView
+ # widget
+
+ @nottest
+ def testCaretOffset(self):
+ "Make sure the caret offset works as expected"
+ self.runDemo('Dialog and Message Boxes')
+ wnd = self.app.window('Dialogs')
+ entry1 = wnd.child(label='Entry 1')
+ entry2 = wnd.child(label='Entry 2')
+
+ # Try reading the entries:
+ self.assertEquals(entry1.text, '')
+ self.assertEquals(entry2.text, '')
+
+ # Set them...
+ s1 = "I just need a sentence"
+ s2 = "And maybe a second one to be sure"
+ entry1.text = s1
+ entry2.text = s2
+
+ # Make sure the caret offset is zero
+ self.assertEquals(entry1.caretOffset, 0)
+ self.assertEquals(entry2.caretOffset, 0)
+
+ # Set the caret offset to something ridiculous
+ entry1.caretOffset = len(s1 * 3)
+ entry2.caretOffset = len(s2 * 3)
+
+ # Make sure the caret offset only goes as far as the end of the string
+ self.assertEquals(entry1.caretOffset, len(s1))
+ self.assertEquals(entry2.caretOffset, len(s2))
+
+ def splitByOffsets(node, string):
+ # Verify the equality of node.text and string, word by word.
+ # I realize this doesn't really test dogtail itself, but that could
+ # change in the future and I don't want to throw the code away.
+ textIface = node.queryText()
+ endOffset = -1 # We only set this now so the loop looks nicer
+ startOffset = 0
+ while startOffset != len(string):
+ (text, startOffset, endOffset) = textIface.getTextAtOffset(
+ startOffset, pyatspi.TEXT_BOUNDARY_WORD_START)
+ self.assertEquals(startOffset,
+ string.find(text, startOffset, endOffset))
+ startOffset = endOffset
+
+ splitByOffsets(entry1, s1)
+ splitByOffsets(entry2, s2)
+
+ # 'combovalue' (read/write string):
+ @nottest
+ def testSetComboValue(self):
+ self.runDemo('Combo boxes')
+ wnd = self.app.window('Combo boxes')
+ combo1 = wnd.child('Some stock icons').child(roleName='combo box')
+ combo1.combovalue = 'Clear'
+ self.assertEquals(combo1.combovalue, 'Clear')
+
+ # 'stateSet' (read-only StateSet instance):
+ def testGetStateSet(self):
+ "Node.sensitive should be False for the gtk-demo app node"
+ self.assert_(not self.app.sensitive)
+
+ def testSetStateSet(self):
+ "Node.stateSet should be read-only"
+ # FIXME should be AttributeError?
+ self.assertRaises(
+ RuntimeError, self.app.__setattr__, "states", pyatspi.StateSet())
+
+ # 'relations' (read-only list of atspi.Relation instances):
+ def testGetRelations(self):
+ # FIXME once relations are used for something other than labels
+ pass
+
+ # 'labelee' (read-only list of Node instances):
+ @nottest
+ def testGetLabelee(self):
+ "Entry1/2's labelee should be a text widget"
+ self.runDemo('Dialog and Message Boxes')
+ wnd = self.app.window('Dialogs')
+ label = wnd.child(roleName='label')
+ self.assertEquals(label.labelee.roleName, 'text')
+
+ def testSetLabelee(self):
+ "Node.labelee should be read-only"
+ self.assertRaises(
+ AttributeError, self.app.__setattr__, "labellee", None)
+
+ # 'labeler' (read-only list of Node instances):
+ # def testGetLabeler(self):
+ # "The text areas in the 'Dialogs' window should have labelers."
+ # self.runDemo('Dialog and Message Boxes')
+ # wnd = self.app.window('Dialogs')
+ # text = wnd.child(roleName = 'text')
+ # self.assertEquals(text.labeler.name, 'Entry 2')
+
+ def testSetLabeller(self):
+ "Node.labeller should be read-only"
+ self.assertRaises(
+ AttributeError, self.app.__setattr__, "labeller", None)
+
+ # 'sensitive' (read-only boolean):
+ def testGetSensitive(self):
+ """
+ Node.sensitive should not be set for the gtk-demo app.
+ It should be set for the window within the app.
+ """
+ self.assert_(not self.app.sensitive)
+ self.assert_(self.app.children[0].sensitive)
+
+ def testSetSensitive(self):
+ "Node.sensitive should be read-only"
+ self.assertRaises(
+ AttributeError, self.app.__setattr__, "sensitive", True)
+
+ # 'showing' (read-only boolean):
+ def testGetShowing(self):
+ "Node.showing should not be set for the gtk-demo. It should be set for the window within the app"
+ self.assert_(not self.app.showing)
+ self.assert_(self.app.children[0].showing)
+
+ def testSetShowing(self):
+ "Node.showing should be read-only"
+ self.assertRaises(
+ AttributeError, self.app.__setattr__, "showing", True)
+
+ # 'actions' (read-only list of Action instances):
+ def testGetActions(self):
+ "Node.actions should be an empty list for the app node"
+ self.assertEquals(len(self.app.actions), 0)
+
+ def testSetActions(self):
+ "Node.actions should be read-only"
+ self.assertRaises(AttributeError, self.app.__setattr__, "actions", {})
+
+ # 'extents' (readonly tuple):
+ def testGetExtents(self):
+ "Node.extents should be a 4-tuple for a window, with non-zero size"
+ (x, y, w, h) = self.app.children[0].extents
+ self.assert_(w > 0)
+ self.assert_(h > 0)
+
+ def testSetExtents(self):
+ "Node.extents should be read-only"
+ self.assertRaises(
+ AttributeError, self.app.__setattr__, "extents", (0, 0, 640, 480))
+
+ # 'position' (readonly tuple):
+ def testGetPosition(self):
+ "Node.position should be a 2-tuple for a window"
+ (x, y) = self.app.children[0].position
+
+ def testSetPosition(self):
+ "Node.position should be read-only"
+ self.assertRaises(
+ AttributeError, self.app.__setattr__, "position", (0, 0))
+
+ # 'size' (readonly tuple):
+ def testGetSize(self):
+ "Node.size should be a 2-tuple for a window, with non-zero values"
+ (w, h) = self.app.children[0].size
+ self.assert_(w > 0)
+ self.assert_(h > 0)
+
+ def testSetSize(self):
+ "Node.size should be read-only"
+ self.assertRaises(
+ AttributeError, self.app.__setattr__, "size", (640, 480))
+
+ # 'toolkitName' (readonly string):
+ def testGetToolkit(self):
+ self.assertEquals(self.app.toolkitName, "gtk")
+
+ def testSetToolkit(self):
+ "Node.toolkit should be read-only"
+ self.assertRaises(
+ AttributeError, self.app.__setattr__, "toolkitName", "gtk")
+
+ # 'ID'
+ def testGetID(self):
+ "Node.id should be numeric"
+ self.assertEquals(type(self.app.id), type(42))
+
+ def testSetID(self):
+ "Node.id should be read-only"
+ self.assertRaises(AttributeError, setattr, self.app, "id", 42)
+
+
+class TestSelection(GtkDemoTest):
+
+ def testTabs(self):
+ """
+ Tabs in the gtk-demo should be selectable, and be queryable for
+ "isSelected", and the results should change as they are selected.
+ """
+ # Use the Info/Source tabs of gtk-demo:
+ info = self.app.child('Info')
+ source = self.app.child('Source')
+
+ # Check initial state:
+ self.assert_(info.isSelected)
+ self.assert_(not source.isSelected)
+
+ # Select other tab:
+ source.select()
+
+ # Check new state:
+ self.assert_(not info.isSelected, False)
+ self.assert_(source.isSelected)
+
+ # Deselect tab:
+ # source.deselect()
+
+ # Check state:
+ # self.assert_(info.isSelected)
+ #self.assert_(not source.isSelected)
+
+
+class TestValue(GtkDemoTest):
+
+ def testGetValue(self):
+ "The scrollbar starts out at position zero."
+ sb = self.app.child(roleName='scroll bar')
+ self.assertEquals(sb.value, 0)
+
+# def testSetValue(self):
+# "Ensure that we can set the value of the scrollbar."
+# sb = self.app.child(roleName = 'scroll bar')
+# sb.value = 100
+# self.assertEquals(sb.value, 100)
+
+ def testMinValue(self):
+ "Ensure that the minimum value for the scrollbar is correct."
+ sb = self.app.child(roleName='scroll bar')
+ self.assertEquals(sb.minValue, 0)
+
+# def testMaxValue(self):
+# "Ensure that the maximum value for the scrollbar is plausible."
+# sb = self.app.child(roleName = 'scroll bar')
+# self.assert_(sb.maxValue > 250)
+
+ def testMinValueIncrement(self):
+ "Ensure that the minimum value increment of the scrollbar is an int."
+ sb = self.app.child(roleName='scroll bar')
+ self.assertEquals(sb.minValueIncrement, sb.minValueIncrement)
+
+
+class TestSearching(GtkDemoTest):
+ # FIXME: should test the various predicates and the search methods of Node
+
+ def testFindChildren(self):
+ """
+ Ensure that there are the correct number of table cells in the list
+ of demos.
+ """
+ pred = dogtail.predicate.GenericPredicate(roleName='table cell')
+ tableCells = self.app.findChildren(pred)
+
+ def get_table_cells_recursively(node):
+ counter = 0
+ for child in node.children:
+ if child.roleName == 'table cell':
+ counter += 1
+ counter += get_table_cells_recursively(child)
+ return counter
+
+ counter = get_table_cells_recursively(self.app)
+ self.assertEquals(len(tableCells), counter)
+
+ def testFindChildren2(self):
+ "Ensure that there are two tabs in the second page tab list."
+ pred = dogtail.predicate.GenericPredicate(roleName='page tab list')
+ pageTabLists = self.app.findChildren(pred)
+ pred = dogtail.predicate.GenericPredicate(roleName='page tab')
+ # The second page tab list is the one with the 'Info' and 'Source' tabs
+ pageTabs = pageTabLists[1].findChildren(pred)
+ self.assertEquals(len(pageTabs), 6)
+
+ def testFindChildrenLambdas(self):
+ self.runDemo('Dialog and Message Boxes')
+ wnd = self.app.window('Dialogs')
+ texts = wnd.findChildren(lambda x: x.roleName=='text', isLambda = True)
+ self.assertEquals(len(texts), 2)
+ self.assertEquals(texts[0].roleName, 'text')
+ self.assertEquals(texts[1].roleName, 'text')
+ texts1 = wnd.findChildren(lambda x: x.roleName=='text' and x.labeler.name == 'Entry 1', isLambda = True)
+ self.assertEquals(len(texts1), 1)
+ self.assertEquals(texts1[0].roleName, 'text')
+ self.assertEquals(texts1[0].labeler.name, 'Entry 1')
+ texts2 = wnd.findChildren(lambda x: x.roleName=='text' and x.showing, isLambda = True)
+ self.assertEquals(len(texts2), 2)
+ self.assertEquals(texts2[0].roleName, 'text')
+ self.assertTrue(texts2[0].showing)
+ self.assertEquals(texts2[1].roleName, 'text')
+ self.assertTrue(texts2[1].showing)
+
+ # def testFindChildrenNonRecursive(self):
+ # """
+ # Ensure that there are the correct number of table cells in the Tree
+ # Store demo.
+ # """
+ # The next several lines exist to expand the 'Tree View' item and
+ # scroll down, so that runDemo() will work.
+ # FIXME: make runDemo() handle this for us.
+ # treeViewCell = self.app.child('Tree View', roleName = 'table cell')
+ # treeViewCell.typeText('+')
+ # dogtail.tree.doDelay()
+ # sb = self.app.child(roleName = 'scroll bar')
+ # sb.value = sb.maxValue
+ # self.runDemo('Tree Store')
+ # wnd = self.app.window('Card planning sheet')
+ # table = wnd.child(roleName = 'tree table')
+ # pred = dogtail.predicate.GenericPredicate(roleName = 'table cell')
+ # dogtail.config.config.childrenLimit = 10000
+ # cells = table.findChildren(pred, recursive = False)
+ # direct_cells = filter(lambda cell: cell.roleName=='table cell', table.children)
+ # self.assertEquals(len(cells), len(direct_cells))
+
+
+class TestActions(GtkDemoTest):
+ # FIXME: should test the various actions
+ pass
+
+
+class TestProcedural(GtkDemoTest):
+ # FIXME: should test the procedural API
+ pass
+
+
+class TestExceptions(GtkDemoTest):
+
+ @nottest
+ def test_exception(self):
+ # Kill the gtk-demo prematurely:
+ import os
+ import signal
+ os.kill(self.pid, signal.SIGKILL)
+
+ from gi.repository import GLib
+ # Ensure that we get an exception when we try to work further with it:
+ self.assertRaises(GLib.GError, self.app.dump)
+
+
+class TestConfiguration(unittest.TestCase):
+
+ def test_get_set_all_properties(self):
+ for option in dogtail.config.config.defaults.keys():
+ print("Setting config.%s property" % option)
+ value = ''
+ if 'Dir' in option:
+ value = '/tmp/dogtail/' # Special value for dir-related properties
+ dogtail.config.config.__setattr__(option, value)
+ self.assertEquals(dogtail.config.config.__getattr__(option), value)
+
+ def test_default_directories_created(self):
+ import os.path
+ self.assertEquals(
+ os.path.isdir(dogtail.config.config.scratchDir), True)
+ self.assertEquals(os.path.isdir(dogtail.config.config.logDir), True)
+ self.assertEquals(os.path.isdir(dogtail.config.config.dataDir), True)
+
+ def test_set(self):
+ self.assertRaises(
+ AttributeError, setattr, dogtail.config.config, 'nosuchoption', 42)
+
+ def test_get(self):
+ self.assertRaises(
+ AttributeError, getattr, dogtail.config.config, 'nosuchoption')
+
+ def helper_create_directory_and_set_option(self, path, property_name):
+ import os.path
+ if os.path.isdir(path):
+ import shutil
+ shutil.rmtree(path)
+ dogtail.config.config.__setattr__(property_name, path)
+ self.assertEquals(os.path.isdir(path), True)
+
+ def test_create_scratch_directory(self):
+ new_folder = "/tmp/dt"
+ self.helper_create_directory_and_set_option(new_folder, 'scratchDir')
+
+ def test_create_data_directory(self):
+ new_folder = "/tmp/dt_data"
+ self.helper_create_directory_and_set_option(new_folder, 'dataDir')
+
+ def test_create_log_directory(self):
+ new_folder = "/tmp/dt_log"
+ self.helper_create_directory_and_set_option(new_folder, 'logDir')
+
+ def test_load(self):
+ dogtail.config.config.load({'actionDelay': 2.0})
+ self.assertEquals(dogtail.config.config.actionDelay, 2.0)
+
+ def test_reset(self):
+ default_actionDelay = dogtail.config.config.defaults['actionDelay']
+ dogtail.config.config.actionDelay = 2.0
+ dogtail.config.config.reset()
+ self.assertEquals(
+ dogtail.config.config.actionDelay, default_actionDelay)
+
+
+class TestDump(GtkDemoTest):
+
+ @nottest
+ def test_dump_to_stdout(self):
+ child = self.app.child('Source')
+ output = trap_stdout(child.dump)
+ self.assertEquals(
+ output,
+ """[page tab | Source]
+ [scroll pane | ]
+ [text | ]
+ [scroll bar | ]
+ [action | activate | ]
+ [scroll bar | ]
+ [action | activate | ]""")