diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/gtkdemotest.py | 56 | ||||
-rw-r--r-- | tests/test_config.py | 66 | ||||
-rw-r--r-- | tests/test_logging.py | 77 | ||||
-rw-r--r-- | tests/test_node.py | 570 | ||||
-rw-r--r-- | tests/test_predicate.py | 192 | ||||
-rw-r--r-- | tests/test_procedural.py | 213 | ||||
-rw-r--r-- | tests/test_utils.py | 64 |
7 files changed, 1238 insertions, 0 deletions
diff --git a/tests/gtkdemotest.py b/tests/gtkdemotest.py new file mode 100644 index 00000000000..2caa0b4824c --- /dev/null +++ b/tests/gtkdemotest.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +import unittest + + +class GtkDemoTest(unittest.TestCase): + """ + TestCase subclass which handles bringing up and shutting down gtk-demo as a fixture. Used for writing other test cases. + """ + + def setUp(self): + import dogtail.config + dogtail.config.config.logDebugToStdOut = True + dogtail.config.config.logDebugToFile = False + import dogtail.utils + self.pid = dogtail.utils.run('gtk3-demo') + self.app = dogtail.tree.root.application('gtk3-demo') + + def tearDown(self): + import os + import signal + import time + os.kill(self.pid, signal.SIGKILL) + # Sleep just enough to let the app actually die. + # AT-SPI doesn't like being hammered too fast. + time.sleep(0.5) + + def runDemo(self, demoName): + """ + Click on the named demo within the gtk-demo app. + """ + tree = self.app.child(roleName="tree table") + tree.child(demoName).doActionNamed('activate') + + +def trap_stdout(function, args=None): + """ + Grab stdout output during function execution + """ + + import sys + from StringIO import StringIO + + saved_stdout = sys.stdout + try: + out = StringIO() + sys.stdout = out + if type(args) is dict: + function(**args) + elif args: + function(args) + else: + function() + output = out.getvalue().strip() + finally: + sys.stdout = saved_stdout + return output diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 00000000000..7a3a44f46e1 --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Unit tests for the dogtail.config class +""" + +import unittest +import dogtail.config + + +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) diff --git a/tests/test_logging.py b/tests/test_logging.py new file mode 100644 index 00000000000..848c736a8d2 --- /dev/null +++ b/tests/test_logging.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Unit tests for the dogtail.logging package +""" +import unittest +import dogtail.tree +from gtkdemotest import trap_stdout + + +class TestLogging(unittest.TestCase): + + def setUp(self): + self.old_log_dir = dogtail.config.config.logDir + + def tearDown(self): + dogtail.config.config.logDebugToFile = False + dogtail.config.config.logDir = self.old_log_dir + + def test_entryStamp_is_not_empty(self): + ts = dogtail.logging.TimeStamp() + self.assertEquals(len(ts.entryStamp()) > 0, True) + + def test_correct_error_if_log_dir_does_not_exist(self): + import shutil + shutil.rmtree(dogtail.config.config.logDir) + self.assertRaises(IOError, dogtail.logging.Logger, "log", file=True) + + def test_unique_name(self): + logger1 = dogtail.logging.Logger("log", file=True) + logger1.createFile() + logger2 = dogtail.logging.Logger("log", file=True) + logger2.createFile() + logger3 = dogtail.logging.Logger("log", file=True) + self.assertNotEquals(logger1.fileName, logger2.fileName) + self.assertNotEquals(logger2.fileName, logger3.fileName) + + def test_no_new_line_to_file(self): + dogtail.config.config.logDebugToFile = True + logger = dogtail.logging.Logger("log", file=True, stdOut=False) + logger.log("hello world", newline=False) + self.assertTrue("hello world " in open(logger.fileName, 'r').read()) + + def test_no_new_line_to_stdout(self): + dogtail.config.config.logDebugToFile = False + logger = dogtail.logging.Logger("log", file=False, stdOut=True) + output = trap_stdout( + logger.log, {'message': 'hello world', 'newline': False}) + self.assertEquals(output, "hello world") + + def test_no_new_line_to_both_file_and_stdout(self): + dogtail.config.config.logDebugToFile = True + logger = dogtail.logging.Logger("log", file=True, stdOut=True) + output = trap_stdout( + logger.log, {'message': 'hello world', 'newline': False}) + self.assertTrue("hello world" in output) + self.assertTrue("hello world " in open(logger.fileName, 'r').read()) + + def test_empty_script_name(self): + dogtail.config.config.scriptName = None + logger = dogtail.logging.Logger("log", file=True, stdOut=True) + self.assertTrue(logger.fileName, "log") + + def test_force_to_file(self): + dogtail.config.config.logDebugToFile = False + logger = dogtail.logging.Logger("log", file=True, stdOut=False) + logger.log("hello world", force=True) + self.assertTrue("hello world" in open(logger.fileName, 'r').read()) + + def test_results_logger_correct_dict(self): + logger = dogtail.logging.ResultsLogger("log") + output = trap_stdout(logger.log, {'entry': {'a': '1'}}) + self.assertEquals('a: 1' in output, True) + + def test_results_logger_incorrect_dict(self): + logger = dogtail.logging.ResultsLogger("log") + self.assertRaises(ValueError, logger.log, "not a dict") 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 | ]""") diff --git a/tests/test_predicate.py b/tests/test_predicate.py new file mode 100644 index 00000000000..a1287d2c29a --- /dev/null +++ b/tests/test_predicate.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Unit tests for the dogtail.predicate package +""" +import unittest +import dogtail.tree +import dogtail.predicate + + +class TestPredicate(unittest.TestCase): + + class DummyNode: + + def __init__(self, name='', roleName='', description=''): + self.name = name + self.roleName = roleName + self.description = description + self.labeller = None + + def test_capitalization(self): + self.assertEquals( + dogtail.predicate.makeCamel("gnome-terminal"), "gnomeTerminal") + self.assertEquals( + dogtail.predicate.makeCamel("Evolution - Mail"), "evolutionMail") + self.assertEquals( + dogtail.predicate.makeCamel( + 'self.assertEquals(makeCamel("Evolution - Mail"), "evolutionMail")'), + "selfAssertequalsMakecamelEvolutionMailEvolutionmail") + + def test_abstract_class(self): + predicate = dogtail.predicate.Predicate() + self.assertRaises(NotImplementedError, predicate.satisfiedByNode, None) + self.assertRaises( + NotImplementedError, predicate.makeScriptMethodCall, None) + self.assertRaises( + NotImplementedError, predicate.makeScriptVariableName) + self.assertRaises( + NotImplementedError, predicate.describeSearchResult, None) + + def test_correct_equality(self): + predicate1 = dogtail.predicate.Predicate() + predicate2 = dogtail.predicate.Predicate() + self.assertEquals(predicate1, predicate2) + + def test_incorrect_equality(self): + predicate = dogtail.predicate.Predicate() + self.assertNotEquals(predicate, self) + + def test_predicates_application(self): + dummyApp = self.DummyNode('dummy', 'application') + appPredicate = dogtail.predicate.IsAnApplicationNamed(dummyApp.name) + self.assertTrue(appPredicate.satisfiedByNode(dummyApp)) + self.assertEquals( + appPredicate.makeScriptMethodCall(True), u'application("dummy")') + self.assertEquals(appPredicate.makeScriptVariableName(), u'dummyApp') + + def test_predicates_window(self): + dummyWin = self.DummyNode('dummy', 'frame') + self.assertTrue( + dogtail.predicate.IsAWindow().satisfiedByNode(dummyWin)) + self.assertEquals( + dogtail.predicate.IsAWindow().describeSearchResult(), 'window') + + def test_predicates_window_named(self): + dummyWin = self.DummyNode('dummy', 'frame') + frameNamedPredicate = dogtail.predicate.IsAWindowNamed(dummyWin.name) + self.assertTrue(frameNamedPredicate.satisfiedByNode(dummyWin)) + self.assertEquals( + frameNamedPredicate.makeScriptMethodCall(False), u'window("dummy")') + self.assertEquals( + frameNamedPredicate.makeScriptVariableName(), u'dummyWin') + + def test_predicates_menu_named(self): + dummyMenu = self.DummyNode('dummy', 'menu') + menuNamedPredicate = dogtail.predicate.IsAMenuNamed(dummyMenu.name) + self.assertTrue(menuNamedPredicate.satisfiedByNode(dummyMenu)) + self.assertEquals(menuNamedPredicate.makeScriptMethodCall( + False), u'menu("dummy", recursive=False)') + self.assertEquals( + menuNamedPredicate.makeScriptVariableName(), u'dummyMenu') + + def test_predicates_menu_item_named(self): + dummyMenuItem = self.DummyNode('dummy', 'menu item') + menuItemNamedPredicate = dogtail.predicate.IsAMenuItemNamed( + dummyMenuItem.name) + self.assertTrue(menuItemNamedPredicate.satisfiedByNode(dummyMenuItem)) + self.assertEquals(menuItemNamedPredicate.makeScriptMethodCall( + False), u'menuItem("dummy", recursive=False)') + self.assertEquals( + menuItemNamedPredicate.makeScriptVariableName(), u'dummyMenuItem') + + def test_predicates_text_entry_named(self): + dummyText = self.DummyNode('dummy', 'text') + textNamedPredicate = dogtail.predicate.IsATextEntryNamed( + dummyText.name) + self.assertTrue(textNamedPredicate.satisfiedByNode(dummyText)) + self.assertEquals(textNamedPredicate.makeScriptMethodCall( + False), u'textentry("dummy", recursive=False)') + self.assertEquals( + textNamedPredicate.makeScriptVariableName(), u'dummyEntry') + + def test_predicates_button_named(self): + dummyButton = self.DummyNode('dummy', 'push button') + buttonNamedPredicate = dogtail.predicate.IsAButtonNamed( + dummyButton.name) + self.assertTrue(buttonNamedPredicate.satisfiedByNode(dummyButton)) + self.assertEquals(buttonNamedPredicate.makeScriptMethodCall( + False), u'button("dummy", recursive=False)') + self.assertEquals( + buttonNamedPredicate.makeScriptVariableName(), u'dummyButton') + + def test_predicates_page_tab_named(self): + dummyTab = self.DummyNode('dummy', 'page tab') + pageTabNamedPredicate = dogtail.predicate.IsATabNamed(dummyTab.name) + self.assertTrue(pageTabNamedPredicate.satisfiedByNode(dummyTab)) + self.assertEquals(pageTabNamedPredicate.makeScriptMethodCall( + False), u'tab("dummy", recursive=False)') + self.assertEquals( + pageTabNamedPredicate.makeScriptVariableName(), u'dummyTab') + + def test_predicates_generic_by_name(self): + dn1 = self.DummyNode('dummy name 1', 'dummy role 1', 'dummy desc 1') + genericPredicateByName = dogtail.predicate.GenericPredicate( + name=dn1.name) + self.assertTrue(genericPredicateByName.satisfiedByNode(dn1)) + self.assertEquals(genericPredicateByName.makeScriptMethodCall( + False), u'child( name="dummy name 1", recursive=False)') + self.assertEquals( + genericPredicateByName.makeScriptVariableName(), u'dummyName1Node') + + def test_predicates_generic_by_roleName(self): + dn1 = self.DummyNode('dummy name 1', 'dummy role 1', 'dummy desc 1') + genericPredicateByRole = dogtail.predicate.GenericPredicate( + roleName=dn1.roleName) + self.assertTrue(genericPredicateByRole.satisfiedByNode(dn1)) + self.assertEquals(genericPredicateByRole.makeScriptMethodCall( + False), u"child( roleName='dummy role 1', recursive=False)") + self.assertEquals( + genericPredicateByRole.makeScriptVariableName(), u'dummyRole1Node') + + def test_predicates_generic_by_description(self): + dn1 = self.DummyNode('dummy name 1', 'dummy role 1', 'dummy desc 1') + genericPredicateByDescription = dogtail.predicate.GenericPredicate( + description=dn1.description) + self.assertTrue(genericPredicateByDescription.satisfiedByNode(dn1)) + self.assertEquals(genericPredicateByDescription.makeScriptMethodCall( + False), u"child( description='dummy desc 1', recursive=False)") + self.assertEquals( + genericPredicateByDescription.makeScriptVariableName(), u'dummyDesc1Node') + + def test_predicates_generic_by_label(self): + dn1 = self.DummyNode('dummy name 1', 'dummy role 1', 'dummy desc 1') + dn2 = self.DummyNode('dummy name 2', 'dummy role 2', 'dummy desc 2') + dn2.labeller = dn1 + genericPredicateByLabel = dogtail.predicate.GenericPredicate( + label=dn1.name) + self.assertTrue(genericPredicateByLabel.satisfiedByNode(dn2)) + self.assertEquals(genericPredicateByLabel.makeScriptMethodCall( + False), u'child(label="dummy name 1", recursive=False)') + self.assertEquals( + genericPredicateByLabel.makeScriptVariableName(), u'dummyName1Node') + + def test_predicates_named(self): + dn1 = self.DummyNode('dummy name 1', 'dummy role 1', 'dummy desc 1') + genericNamedPredicate = dogtail.predicate.IsNamed(dn1.name) + self.assertTrue(genericNamedPredicate.satisfiedByNode(dn1)) + self.assertEquals(genericNamedPredicate.makeScriptMethodCall( + False), u'child(name="dummy name 1", recursive=False)') + self.assertEquals( + genericNamedPredicate.makeScriptVariableName(), u'dummyName1Node') + + def test_predicates_labelled_as(self): + dn1 = self.DummyNode('dummy name 1', 'dummy role 1', 'dummy desc 1') + dn2 = self.DummyNode('dummy name 2', 'dummy role 2', 'dummy desc 2') + dn2.labeller = dn1 + genericLabelledPredicate = dogtail.predicate.IsLabelledAs(dn1.name) + self.assertTrue(genericLabelledPredicate.satisfiedByNode(dn2)) + self.assertFalse(genericLabelledPredicate.satisfiedByNode(dn1)) + self.assertEquals(genericLabelledPredicate.makeScriptMethodCall( + False), u'child(label="dummy name 1", recursive=False)') + self.assertEquals( + genericLabelledPredicate.makeScriptVariableName(), u'dummyName1Node') + + def test_predicates_dialog_named(self): + dn1 = self.DummyNode('dummy name 1', 'dialog', 'dummy desc 1') + genericNamedPredicate = dogtail.predicate.IsADialogNamed(dn1.name) + self.assertTrue(genericNamedPredicate.satisfiedByNode(dn1)) + self.assertEquals(genericNamedPredicate.makeScriptMethodCall( + False), u'dialog("dummy name 1")') + self.assertEquals( + genericNamedPredicate.makeScriptVariableName(), u'dummyName1Dlg') diff --git a/tests/test_procedural.py b/tests/test_procedural.py new file mode 100644 index 00000000000..820678a37b1 --- /dev/null +++ b/tests/test_procedural.py @@ -0,0 +1,213 @@ +#!/usr/bin/python +""" +Unit tests for the dogtail.procedural API +""" +__author__ = "Zack Cerza <zcerza@redhat.com>" + +from dogtail.procedural import focus, keyCombo, deselect, select, click, tree, FocusError, run, config, type +config.logDebugToFile = False +config.logDebugToStdOut = True +import pyatspi +from gtkdemotest import GtkDemoTest, trap_stdout +from nose.tools import nottest + + +class GtkDemoTest(GtkDemoTest): + + def setUp(self): + self.pid = run('gtk3-demo') + self.app = focus.application.node + + # FIXME: Implement doubleclick() in d.procedural and override the other + # methods of Node.GtkDemoTest + + +class TestFocusApplication(GtkDemoTest): + + def testFocusingBogusNameWithoutAFatalError(self): + config.fatalErrors = False + output = trap_stdout(focus.application, "should not be found") + self.assertTrue( + 'The requested widget could not be focused: "should not be found" application' in output) + + def testThrowExceptionOnFocusingBogusName(self): + config.fatalErrors = True + self.assertRaises(FocusError, focus.application, "should not be found") + + def testFocusingBasic(self): + "Ensure that focus.application() sets focus.application.node properly" + focus.application.node = None + focus.application("gtk3-demo") + self.assertEquals(focus.application.node, self.app) + + +class TestFocusWindow(GtkDemoTest): + + def testFocusingBogusNameWithoutAFatalError(self): + config.fatalErrors = False + output = trap_stdout(focus.window, "should not be found") + self.assertEquals(focus.window.node, None) + self.assertTrue( + 'The requested widget could not be focused: "should not be found" window' in output) + + def testThrowExceptionOnFocusingBogusName(self): + config.fatalErrors = True + self.assertRaises(FocusError, focus.window, "should not be found") + + +class TestFocusDialog(GtkDemoTest): + + def testFocusingBogusNameWithoutAFatalError(self): + config.fatalErrors = False + output = trap_stdout(focus.dialog, "should not be found") + self.assertEquals(focus.dialog.node, None) + self.assertTrue( + 'The requested widget could not be focused: "should not be found" dialog' in output) + + def testThrowExceptionOnFocusingBogusName(self): + config.fatalErrors = True + self.assertRaises(FocusError, focus.dialog, "should not be found") + + +class TestFocusWidget(GtkDemoTest): + + def testFocusingEmptyName(self): + self.assertRaises(TypeError, focus.widget) + + def testFocusingBogusNameWithoutAFatalError(self): + config.fatalErrors = False + output = trap_stdout(focus.widget, "should not be found") + self.assertEquals(focus.widget.node, None) + self.assertTrue( + 'The requested widget could not be focused: child with name="should not be found"' in output) + + def testThrowExceptionOnFocusingBogusName(self): + config.fatalErrors = True + self.assertRaises(FocusError, focus.widget, "should not be found") + + def testFocusingBasic(self): + "Ensure that focus.widget('foo') finds a node with name 'foo'" + focus.widget("Application window") + self.assertEquals(focus.widget.name, "Application window") + + +class TestFocus(GtkDemoTest): + + def testInitialState(self): + "Ensure that focus.widget, focus.dialog and focus.window are None " + \ + "initially." + self.assertEquals(focus.widget.node, None) + self.assertEquals(focus.dialog.node, None) + self.assertEquals(focus.window.node, None) + + def testFocusingApp(self): + "Ensure that focus.app() works" + focus.app.node = None + focus.app('gtk3-demo') + self.assertEquals(focus.app.node, self.app) + + def testFocusingAppViaApplication(self): + "Ensure that focus.application() works" + focus.app.node = None + focus.application('gtk3-demo') + self.assertEquals(focus.app.node, self.app) + + def testFocusGettingBogusAttribute(self): + self.assertRaises(AttributeError, getattr, focus, 'nosuchtype') + + def testFocusSettingBogusAttribute(self): + self.assertRaises( + AttributeError, setattr, focus, 'nosuchtype', 'nothing') + + def testFocusingRoleName(self): + "Ensure that focus.widget(roleName=...) works." + focus.widget(roleName='page tab') + self.assert_(isinstance(focus.widget.node, tree.Node)) + self.assertEquals(focus.widget.node.role, pyatspi.ROLE_PAGE_TAB) + + def testFocusMenu(self): + self.runDemo('Application window') + focus.window('Application Window') + focus.menu('File') + self.assert_(isinstance(focus.widget.node, tree.Node)) + self.assertEquals(focus.widget.node.role, pyatspi.ROLE_MENU) + + def testFocusMenuItem(self): + self.runDemo('Application window') + focus.window('Application Window') + click.menu('File') + focus.menuItem('New') + self.assert_(isinstance(focus.widget.node, tree.Node)) + self.assertEquals(focus.widget.node.role, pyatspi.ROLE_MENU_ITEM) + + def testFocusButton(self): + self.runDemo('Application window') + focus.window('Application Window') + focus.button('Open') + self.assert_(isinstance(focus.widget.node, tree.Node)) + self.assertEquals(focus.widget.node.role, pyatspi.ROLE_PUSH_BUTTON) + + def testFocusTable(self): + self.runDemo('Builder') + focus.window('GtkBuilder demo') + focus.table('') + self.assert_(isinstance(focus.widget.node, tree.Node)) + self.assertEquals(focus.widget.node.role, pyatspi.ROLE_TABLE) + + def testFocusTableCell(self): + self.runDemo('Builder') + focus.window('GtkBuilder demo') + focus.tableCell('') + self.assert_(isinstance(focus.widget.node, tree.Node)) + self.assertEquals(focus.widget.node.role, pyatspi.ROLE_TABLE_CELL) + + def testFocusText(self): + self.runDemo('Application window') + focus.window('Application Window') + focus.text('') + self.assert_(isinstance(focus.widget.node, tree.Node)) + self.assertEquals(focus.widget.node.role, pyatspi.ROLE_TEXT) + + +class TestKeyCombo(GtkDemoTest): + + def testKeyCombo(self): + self.runDemo('Application window') + focus.window('Application Window') + keyCombo("<ctrl>a") + focus.dialog('About GTK+ Code Demos') + + +class TestActions(GtkDemoTest): + + def testClick(self): + click('Source') + self.assertTrue(focus.widget.isSelected) + + def testClickWithRaw(self): + click('Source', raw=True) + self.assertTrue(focus.widget.isSelected) + + def testSelect(self): + select('Source') + self.assertTrue(focus.widget.isSelected) + + @nottest + def testDeselect(self): + type('Icon View') + click('Icon View') + type('+') + self.runDemo('Icon View Basics') + focus.window('GtkIconView demo') + + focus.widget(roleName='icon') + select() + deselect() + self.assertFalse(focus.widget.isSelected) + + def testTyping(self): + self.runDemo('Dialog and Message Boxes') + focus.window('Dialogs') + focus.widget(roleName='text') + type("hello world") + self.assertEquals(focus.widget.node.text, 'hello world') diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 00000000000..02f781bb34c --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,64 @@ +#!/usr/bin/python +""" +Unit tests for the dogtail.procedural API +""" +__author__ = "Zack Cerza <zcerza@redhat.com>" + +import unittest +import dogtail.tree +import dogtail.predicate +dogtail.config.config.logDebugToFile = False +dogtail.config.config.logDebugToStdOut = True +from gtkdemotest import GtkDemoTest + + +class TestScreenshot(GtkDemoTest): + + def make_expected_and_compare(self, actual_path, jpg_tolerance=None): + extension = actual_path.split('.')[-1] + expected_path = actual_path.replace(extension, "expected." + extension) + + import os + os.system("gnome-screenshot -f %s" % expected_path) + + command = ["compare", "-metric", "MAE", + actual_path, expected_path, "output"] + import subprocess + p = subprocess.Popen(command, stderr=subprocess.PIPE) + output, error = p.communicate() + + import re + m = re.search(r"\((.*)\)", error) + self.assertTrue(0.1 >= float(m.group(1))) + + def test_screenshot_incorrect_timestamp(self): + self.assertRaises( + TypeError, dogtail.utils.screenshot, "timeStamp", None) + + def test_screenshot_default(self): + actual_path = dogtail.utils.screenshot() + self.make_expected_and_compare(actual_path) + + def test_screenshot_basename(self): + actual_path = dogtail.utils.screenshot("basename") + self.make_expected_and_compare(actual_path) + + def test_screenshot_no_time_stamp(self): + actual_path = dogtail.utils.screenshot(timeStamp=False) + self.make_expected_and_compare(actual_path) + + def test_screenshot_jpeg(self): + actual_path = dogtail.utils.screenshot("basename.jpg") + self.make_expected_and_compare(actual_path, jpg_tolerance=True) + + def test_screenshot_unknown_format(self): + self.assertRaises(ValueError, dogtail.utils.screenshot, "basename.dat") + + +class TestA11Y(unittest.TestCase): + + def test_bail_when_a11y_disabled(self): + self.assertRaises(SystemExit, dogtail.utils.bailBecauseA11yIsDisabled) + + def test_enable_a11y(self): + dogtail.utils.enableA11y() |