Commits

Ed Blake committed 9ada73d

Added tests for script and control sub-modules.

Comments (0)

Files changed (8)

 :Description: Python ctypes wrapper around AutoHotKey dll.
 :License: BSD-style, see `LICENSES.txt`_
 :Author: Ed Blake <kitsu.eb@gmail.com>
-:Date: Sep. 17 2012
-:Revision: 10
+:Date: Sep. 19 2012
+:Revision: 12
 
 Introduction
 ------------
 -------
 A helper script "runtests.py" is provided in the project root to run the entire 
 test suite. Runnable test scripts are provided for each sub-module in the test 
-folder. Test coverage is a little light, see ToDo_ list.
+folder. Tests require Michael Foord's `mock library`_.
 
 Usage
 -----
 
 ToDo
 ----
+* Re-write script.Function to use the now working ahk.call function.
+* Extend setup script with options to download/install the correct dll?
 * Add remaining Control commands to Control class.
-* Add doc-tests.
-* Migrate docs to `ReadTheDocs <http://read-the-docs.readthedocs.org/en/latest/getting_started.html>`_?
-* Extend Script class (ahk.execute is annoying).
-* Debug AHK function calling wrappers (can't use arguments).
-* Finish writing low-level tests.
-* Mock out dll so high-level tests can be written (i.e. for winActive et al).
+* Add doc-tests?
+* Migrate docs to ReadTheDocs_? 
+* Extend Script class with something to replace ahk.execute
+  (maybe some kind of subroutine wrapper?).
 * Add examples directory.
 * Add optional unicode support.
 
 .. _LICENSES.txt: https://bitbucket.org/kitsu/pyahk/src/tip/LICENSES.txt
 .. _AutoHotKey.dll: http://www.autohotkey.net/~HotKeyIt/AutoHotkey/files/AutoHotkey-dll-txt.html
 .. _Python2.7: http://python.org/download/releases/2.7.3/#download 
+.. _`mock library`: http://www.voidspace.org.uk/python/mock/
 .. _Docs: http://kitsu_eb.pythonanywhere.com/docs/pyahk
+.. _ReadTheDocs: http://read-the-docs.readthedocs.org/en/latest/getting_started.html
 from script import Function, Script
 from control import Control
 
-__version__ = "0.2.0"
+__version__ = "0.2.1"
 
 def find_func(name):
     """Wrapper around ahkFindFunc.
 
+    .. warning::
+        The following doesn't seem to work, any advice welcome.
+
     Get a pointer address to the named function.
     To use this you must first create a ctypes CFUNCTYPE prototype::
 
 
         result = func(5)
 
-    .. warning::
-        Calling this with a known good function name hangs the process!
-
     :returns: The address of the function as an integer.
     """
     return int(_ahk.ahkFindFunc(name))
         err = 0.0
         for i in range(3):
             err += abs(c1[i] - c2[i])
-        return err/float(255*3)
+        result = err/float(255*3)
+        #print c1, c2, result
+        return result
 
     def getPixel(self, x=0, y=0, opt='RGB', screen=True):
         """Convenince wrapper around ahk PixelGetColor
    * :func:`.execute`
    * :func:`.jump`
    * :func:`.call`
+   * :func:`.post`
    * :func:`.set`
    * :func:`.get`
    * :func:`.terminate`
 
 -------------------------------------------------------------------------------
 
+.. autofunction:: ahk.post
+
+-------------------------------------------------------------------------------
+
 .. autofunction:: ahk.set
 
 -------------------------------------------------------------------------------
 # built documents.
 #
 # The short X.Y version.
-version = '0.2.0'
+version = ahk.__version__
 # The full version, including alpha/beta/rc tags.
-release = '0.2.0'
+release = ahk.__version__
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.

test/test_control.py

 """Test AutoHotKey Control wrapper."""
-import os, time
+import os, time#, random
 import unittest
 try:
+    # The mock library was accepted as part of the Python Std. Library in V3.3
+    from unittest import mock
+except ImportError:
+    # Fall back to the back-ported version
+    import mock
+try:
     import ahk
 except ImportError:
     # Try adding parent folder to front of path
     sys.path = [os.path.abspath("../")] + sys.path
     import ahk
 
+# The methods of the Control class are not well suited to unit-tests as by their
+# nature they interact with the windowing environment they are run in.
 class Test_Control(unittest.TestCase):
     """Test ahk control access wrapper object."""
-    pass
+
+    def setUp(self):
+        """Configure test environment."""
+        self.script = ahk.Script()
+
+    def test_00_delay(self):
+        """Testing control delay decorator."""
+        ctl = ahk.Control(self.script, store=False)
+        # Set the control delay and check that it is set
+        delay = 50
+        ctl.set_delay(control=delay)
+        self.assertEqual(ctl._cdelay, delay, msg="Set delay not stored?")
+        # Mock out ahk.execute in the control module so no commands actually run
+        with mock.patch('ahk.control.execute') as exc:
+            outer_delay = ahk.get("A_ControlDelay")
+            inner_delay = list() # Object in outer scope
+            def store(cmd):
+                """execute replacement for mock."""
+                # Object in outer scope used in closure to store a value
+                inner_delay.append(int(ahk.get("A_ControlDelay")))
+                if 'send' not in cmd.lower():
+                    #print cmd, inner_delay
+                    ahk.execute(cmd)
+
+            exc.side_effect = store # set store to be called when execute is
+            # Call a delayed method and ensure that the stored delay is
+            # in effect only in the method call
+            ctl.send()
+            inner_delay = inner_delay[-1] # Unwrap inner value
+            self.assertNotEqual(outer_delay, inner_delay,
+                msg="Inner delay {0} is the same as outer {1}!".format(
+                    inner_delay, outer_delay))
+            self.assertEqual(inner_delay, delay,
+                msg="Inner delay {0} different than previously set {1}!".format(
+                    inner_delay, delay))
+
+    def test_01_init(self):
+        """Testing control initialization."""
+        # Mock out script instance
+        scr = mock.Mock(spec=self.script)
+        # Set winExist return to known value
+        def effect(title, *args, **kwargs):
+            if 'error' in title.lower():
+                return None
+            return 42
+        scr.winExist.side_effect = effect
+        # First test no store and parameter set
+        inparams = ('title', 'text', 'extitle', 'extext')
+        ctl = ahk.Control(scr, *inparams, store=False)
+        outparams = ctl._params() #NOTE use of private method could break!
+        self.assertEqual(inparams, outparams,
+            msg="Retrieved params {0} don't match input {1}!".format(
+                outparams, inparams))
+        # Next check HWND storage behavior
+        with self.assertRaises(NameError):
+            ctl = ahk.Control(scr, title='error') # store=True but window doesn't exist
+        scr.reset_mock()
+        ctl = ahk.Control(scr, *inparams)
+        self.assertEqual(ctl.hwnd, 42, msg="Wrong HWND stored?")
+        outparams = ctl._params() #NOTE use of private method could break!
+        title = outparams[0].lower()
+        self.assertTrue('ahk_id' in title and '42' in title,
+            msg="HWND param \"{0}\" incorrectly formatted!".format(title))
+        self.assertFalse(''.join(outparams[1:]), # Extra params are set to ''
+            msg="Retrieved params {0} contain unexpected value!".format(
+                outparams[1:]))
+
+    def tearDown(self):
+        """Clean test environment."""
+        self.script = None
 
 # Assemble test suites
 control_suite = unittest.TestLoader().loadTestsFromTestCase(Test_Control)

test/test_script.py

 """Test AutoHotKey script wrappers."""
-import os, time
+import os, time, random
 import unittest
 try:
+    # The mock library was accepted as part of the Python Std. Library in V3.3
+    from unittest import mock
+except ImportError:
+    # Fall back to the back-ported version
+    import mock
+try:
     import ahk
 except ImportError:
     # Try adding parent folder to front of path
 class Test_Function(unittest.TestCase):
     """Test ahk function wrapper object."""
 
-class Test_Script(unittest.TestCase):
-    """Test ahk script wrapper object."""
-
-    def setUp(self):
-        """Configure test environment."""
-        pass
-
-    def test_0_Function(self):
+    def test_00_Function(self):
         """Testing Function wrapper object."""
         tests = (
             (1, 1),
         # Cleanup
         ahk.terminate()
 
-    def test_1_ScriptFunction(self):
+# The methods of the Script class are not well suited to unit-tests as by their
+# nature they interact with the windowing environment they are run in.
+class Test_Script(unittest.TestCase):
+    """Test ahk script wrapper object."""
+
+    def setUp(self):
+        """Configure test environment."""
+        pass
+
+    def test_00_init(self):
+        """Testing that script was initialized correctly."""
+        scr = ahk.Script()
+        self.assertTrue(ahk.ready(nowait=True),
+            msg="AHK not ready after init?")
+        self.assertIsNotNone(scr.Clipboard, 
+            msg="Special Clipboard variable not initialized!")
+        self.assertIsNotNone(scr.ErrorLevel, 
+            msg="Special ErrorLevel variable not initialized!")
+
+    def test_01_ScriptFunction(self):
         """Testing creating and using a Function through a Script object."""
         tests = (
             (1, 1),
             # Error during type conversion
             script.add('abc', 'efg')
 
-    def test_2_ScriptVariable(self):
+    def test_02_ScriptVariable(self):
         """Testing creating and using variables through a Script object."""
         value = 5
         script = ahk.Script()
              msg="Variable value {0} doesn't match expected {1}!".format(
                  script.test, value+5))
 
-    def test_3_winActive(self):
+    def test_03_convert_color(self):
+        """Testing conversion of hex color."""
+        scr = ahk.Script()
+        for i in range(10):
+            t = tuple([random.randint(0, 255) for i in range(3)])
+            h = '0x' + ''.join('{0:02x}'.format(c) for c in t)
+            res = scr.convert_color(h)
+            #print res, t, h
+            self.assertEqual(res, t,
+                msg="Re-converted color {0} doesn't match {1} ({2})!".format(
+                    res, t, h))
+
+    def test_04_waitPixel(self):
+        """Testing waiting for a pixel."""
+        # Test waiting for a specific pixel value
+        with mock.patch.object(ahk.Script, 'getPixel') as getpx:
+            target = (10, 10, 10)
+            threshold = 0.005 # 0.5% threshold
+            interval = 0.01 # no wait since values are canned
+            # Setup getpx with canned return values for our test
+            getpx.side_effect = [
+                (50, 50, 50), # Far away
+                (20, 20, 20), # Closer
+                (13, 13, 13), # Just outside threshold
+                (11, 11, 11), # Within threshold
+                (10, 10, 10), # Extra equal value
+            ]
+            # Test our object
+            scr = ahk.Script()
+            scr.waitPixel(color=target, threshold=threshold, interval=interval)
+            self.assertTrue(getpx.called, msg="Mock wasn't called?")
+            self.assertEqual(getpx.call_count, 4,
+                msg="getPixel mock called {0} times instead of 4.".format(
+                    getpx.call_count))
+
+        # Next test waiting for any different pixel value
+        with mock.patch.object(ahk.Script, 'getPixel') as getpx:
+            threshold = 0.008 # 0.8% threshold
+            interval = 0.01 # no wait since values are canned
+            # Setup getpx with canned return values for our test
+            getpx.side_effect = [
+                (10, 10, 10), # Extra equal value
+                (11, 11, 11), # Within threshold
+                (12, 12, 12), # Within threshold
+                (13, 13, 13), # Just outside threshold
+                (50, 50, 50), # Far away
+            ]
+            # Test our object
+            scr = ahk.Script()
+            scr.waitPixel(threshold=threshold, interval=interval)
+            self.assertTrue(getpx.called, msg="Mock wasn't called?")
+            self.assertEqual(getpx.call_count, 4,
+                msg="getPixel mock called {0} times instead of 4.".format(
+                    getpx.call_count))
+
+    def test_05_winActive(self):
         """Testing IfWinActive wrapper."""
         script = ahk.Script()
         # Test that non-existent window isn't found
         self.assertNotEqual(result, None,
                             msg="Can't find an active window?")
 
-    def test_4_winExist(self):
+    def test_06_winExist(self):
         """Testing IfWinExist wrapper."""
         script = ahk.Script()
         # Test that non-existent window isn't found
         pass
 
 # Assemble test suites
-function_suite = unittest.TestLoader().loadTestsFromTestCase(Test_Functions)
+function_suite = unittest.TestLoader().loadTestsFromTestCase(Test_Function)
 script_suite = unittest.TestLoader().loadTestsFromTestCase(Test_Script)
 all_tests = unittest.TestSuite([
                                 function_suite,