# GUI Application automation and testing library
# Copyright (C) 2006-2018 Mark Mc Mahon and Contributors
# https://github.com/pywinauto/pywinauto/graphs/contributors
# http://pywinauto.readthedocs.io/en/latest/credits.html
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of pywinauto nor the names of its
#   contributors may be used to endorse or promote products derived from
#   this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#pylint: disable-msg=C0301
#pylint: disable-msg=F0401
#pylint: disable-msg=W0142

"""Tests for application.py"""

import sys
import os
import unittest
import time
#import pprint
#import pdb
import warnings
from threading import Thread
import ctypes
import mock
import six

sys.path.append(".")
from pywinauto import Desktop
from pywinauto.windows import application, win32defines
from pywinauto.controls import hwndwrapper
from pywinauto.windows.application import Application
from pywinauto.base_application import WindowSpecification  # noqa: E402
from pywinauto.windows.application import process_module
from pywinauto.windows.application import process_get_modules
from pywinauto.windows.application import ProcessNotFoundError
from pywinauto.windows.application import AppStartError
from pywinauto.windows.application import AppNotConnected
from pywinauto.controls.common_controls import TrackbarWrapper
from pywinauto import findwindows
from pywinauto import findbestmatch
from pywinauto.timings import Timings
from pywinauto.timings import TimeoutError
from pywinauto.timings import WaitUntil
from pywinauto.timings import always_wait_until
from pywinauto.timings import always_wait_until_passes
from pywinauto.timings import timestamp  # noqa: E402
from pywinauto.sysinfo import is_x64_Python
from pywinauto.sysinfo import is_x64_OS
from pywinauto.sysinfo import UIA_support

#application.set_timing(1, .01, 1, .01, .05, 0, 0, .1, 0, .01)

# About dialog may take some time to load
# so make sure that we wait for it.
Timings.window_find_timeout = 5

def _notepad_exe():
    if is_x64_Python() or not is_x64_OS():
        return r"C:\Windows\System32\notepad.exe"
    else:
        return r"C:\Windows\SysWOW64\notepad.exe"

mfc_samples_folder_32 = mfc_samples_folder = os.path.join(
   os.path.dirname(__file__), r"..\..\apps\MFC_samples")
if is_x64_Python():
    mfc_samples_folder = os.path.join(mfc_samples_folder, 'x64')


class ApplicationWarningTestCases(unittest.TestCase):

    """Unit tests for warnings in the application.Application class"""

    def setUp(self):
        """Set some data and ensure the application is in the state we want"""
        Timings.defaults()
        # Force Display User and Deprecation warnings every time
        # Python 3.3 + nose/unittest tries really hard to suppress them
        for warning in (UserWarning, PendingDeprecationWarning):
            warnings.simplefilter('always', warning)

        if is_x64_Python():
            self.sample_exe = os.path.join(mfc_samples_folder,
                                           "CmnCtrl1.exe")
            self.sample_exe_inverted_bitness = os.path.join(mfc_samples_folder_32,
                                                            "CmnCtrl1.exe")
        else:
            self.sample_exe = os.path.join(mfc_samples_folder_32, "CmnCtrl1.exe")
            self.sample_exe_inverted_bitness = os.path.join(mfc_samples_folder,
                                                            "x64",
                                                            "CmnCtrl1.exe")

    def testStartWarning3264(self):
        if not is_x64_OS():
            self.defaultTestResult()
            return

        warnings.filterwarnings('always', category=UserWarning, append=True)
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")
            app = Application().start(self.sample_exe_inverted_bitness)
            app.kill()
            assert len(w) >= 1
            assert issubclass(w[-1].category, UserWarning)
            assert "64-bit" in str(w[-1].message)

    def testConnectWarning3264(self):
        if not is_x64_OS():
            self.defaultTestResult()
            return

        app = Application().start(self.sample_exe_inverted_bitness)
        # Appveyor misteries...
        self.assertEqual(app.is_process_running(), True)

        with mock.patch("warnings.warn") as mockWarn:
            Application().connect(pid=app.process)
            app.kill()
            args, kw = mockWarn.call_args
            assert len(args) == 2
            assert "64-bit" in args[0]
            assert args[1].__name__ == 'UserWarning'


class ApplicationWin32KillTestCases(unittest.TestCase):

    """Unit tests for method Application.kill() with backend='win32'"""

    backend = 'win32'

    def setUp(self):
        """Set some data and ensure the application is in the state we want"""
        Timings.defaults()

        self.sample_exe = os.path.join(mfc_samples_folder, 'RowList.exe')
        self.app = Application(backend=self.backend).start(self.sample_exe)
        self.target_process = self.app.process

    def tearDown(self):
        self.app.kill(soft=False)

    def test_kill_hard(self):
        self.assertTrue(self.app.kill(soft=False))
        self.assertRaises(ProcessNotFoundError, Application().connect, pid=self.target_process)

    def test_kill_soft(self):
        self.assertTrue(self.app.kill(soft=True))
        self.assertRaises(ProcessNotFoundError, Application().connect, pid=self.target_process)

    def test_already_killed_hard(self):
        self.assertTrue(self.app.kill(soft=False))
        self.assertRaises(ProcessNotFoundError, Application().connect, pid=self.target_process)
        self.assertTrue(self.app.kill(soft=False)) # already killed, returned True anyway

    def test_already_killed_soft(self):
        self.assertTrue(self.app.kill(soft=False))
        self.assertRaises(ProcessNotFoundError, Application().connect, pid=self.target_process)
        self.assertTrue(self.app.kill(soft=True)) # already killed, returned True anyway

    def test_kill_soft_with_modal_subdialog(self):
        """Kill the app with modal subdialog to cover win.force_close() call"""
        self.app.RowListSampleApplication.menu_select('Help->About RowList...')
        if self.backend == 'win32':
            self.app.window(name='About RowList').wait('visible')
        elif self.backend == 'uia':
            self.app.RowListSampleApplication.by(name='About RowList').wait('visible')
        else:
            raise NotImplementedError('test_kill_soft_with_modal_subdialog: ' \
                'backend "{}" is not supported'.format(self.backend))
        self.assertTrue(self.app.kill(soft=True))
        self.assertRaises(ProcessNotFoundError, Application().connect, pid=self.target_process)
        self.assertTrue(self.app.kill(soft=True)) # already killed, returned True anyway


if UIA_support:
    class ApplicationUiaKillTestCases(ApplicationWin32KillTestCases):

        """Unit tests for method Application.kill() with backend='uia'"""

        backend = 'uia'
        # the same test methods run here


if ctypes.windll.shell32.IsUserAnAdmin() == 0:
    class AdminTestCases(ApplicationWarningTestCases):

        def setUp(self):
            """Set some data and ensure the application is in the state we want"""
            super(AdminTestCases, self).setUp()
            cmd = 'powershell -Command "Start-Process {} -Verb RunAs"'.format(self.sample_exe)
            self.app = Application().start(cmd, wait_for_idle=False)

        def tearDown(self):
            """Close the application after tests"""
            self.app.kill()
            super(AdminTestCases, self).tearDown()

        def test_non_admin_warning(self):
            warnings.filterwarnings('always', category=UserWarning, append=True)
            with warnings.catch_warnings(record=True) as w:
                warnings.simplefilter("always")
                self.app = Application().connect(name="Common Controls Sample", timeout=20)
                assert len(w) >= 1
                assert issubclass(w[-1].category, UserWarning)
                assert "process has no rights" in str(w[-1].message)

        def test_non_admin_click(self):
            self.app = Application().connect(name="Common Controls Sample", timeout=20)
            with self.assertRaises(RuntimeError):
                self.app.CommonControlsSample.OK.click()
            with self.assertRaises(RuntimeError):
                self.app.CommonControlsSample.OK.click_input()
            with self.assertRaises(RuntimeError):
                self.app.CommonControlsSample.TVS_HASBUTTON.check()


class NonAdminTestCases(ApplicationWarningTestCases):

    def setUp(self):
        """Set some data and ensure the application is in the state we want"""
        super(NonAdminTestCases, self).setUp()
        self.app = Application().start(self.sample_exe)

    def tearDown(self):
        """Close the application after tests"""
        self.app.kill()
        super(NonAdminTestCases, self).tearDown()

    def test_both_non_admin(self):
        warnings.filterwarnings('always', category=UserWarning, append=True)
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")
            self.app = Application().connect(name="Common Controls Sample", timeout=5)
            assert len(w) == 0

    def test_both_non_admin_click(self):
        self.app = Application().connect(name="Common Controls Sample", timeout=5)
        self.app.CommonControlsSample.TVS_HASBUTTON.check()
        self.assertEqual(self.app.CommonControlsSample.TVS_HASBUTTON.is_checked(), True)
        self.app.CommonControlsSample.OK.click()
        self.app.CommonControlsSample.wait_not('visible')


class ApplicationTestCases(unittest.TestCase):

    """Unit tests for the application.Application class"""

    def setUp(self):
        """Set some data and ensure the application is in the state we want"""
        Timings.defaults()
        self.prev_warn = warnings.showwarning
        def no_warnings(*args, **kwargs): pass
        warnings.showwarning = no_warnings

        if is_x64_Python() or not is_x64_OS():
            self.notepad_subpath = r"system32\notepad.exe"
        else:
            self.notepad_subpath = r"SysWOW64\notepad.exe"

    def tearDown(self):
        """Close the application after tests"""
        #self.dlg.SendMessage(win32defines.WM_CLOSE)
        warnings.showwarning = self.prev_warn

    def test__init__(self):
        """Verify that Application instance is initialized or not"""
        self.assertRaises(ValueError, Application, backend='unregistered')

    def test__iter__(self):
        """Verify that Application instance is not iterable"""
        app = Application()
        app.start(_notepad_exe())
        with self.assertRaises(NotImplementedError):
            for a in app:
                pass
        app.kill()

    def test_not_connected(self):
        """Verify that it raises when the app is not connected"""
        self.assertRaises (AppNotConnected, Application().__getattribute__, 'Hiya')
        self.assertRaises (AppNotConnected, Application().__getitem__, 'Hiya')
        self.assertRaises (AppNotConnected, Application().window_, name='Hiya')
        self.assertRaises (AppNotConnected, Application().top_window_,)

    def test_start_problem(self):
        """Verify start_ raises on unknown command"""
        self.assertRaises (AppStartError, Application().start, 'Hiya')

    def test_start(self):
        """test start() works correctly"""
        app = Application()
        self.assertEqual(app.process, None)
        app.start(_notepad_exe())
        self.assertNotEqual(app.process, None)

        self.assertEqual(app.UntitledNotepad.process_id(), app.process)

        notepadpath = os.path.join(os.environ['systemroot'], self.notepad_subpath)
        self.assertEqual(str(process_module(app.process)).lower(), str(notepadpath).lower())

        app.UntitledNotepad.menu_select("File->Exit")

    def testStart_bug01(self):
        """On SourceForge forum AppStartError forgot to include %s for application name"""
        app = Application()
        self.assertEqual(app.process, None)
        application.app_start_timeout = 1
        app_name = r"I am not * and Application!/\.exe"
        try:
            app.start(app_name)
        except AppStartError as e:
            self.assertEqual(app_name in str(e), True)

#    def testset_timing(self):
#        """Test that set_timing sets the timing correctly"""
#        prev_timing = (
#            application.window_find_timeout,
#            application.window_retry_interval,
#            application.app_start_timeout,
#            application.exists_timeout,
#            application.exists_retry_interval,
#            hwndwrapper.delay_after_click,
#            hwndwrapper.delay_after_menuselect,
#            hwndwrapper.delay_after_sendkeys_key,
#            hwndwrapper.delay_after_button_click,
#            hwndwrapper.delay_before_after_close_click,
#        )
#        set_timing(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
#
#        self.assertEqual(
#            (
#                application.window_find_timeout,
#                application.window_retry_interval,
#                application.app_start_timeout,
#                application.exists_timeout,
#                application.exists_retry_interval,
#                hwndwrapper.delay_after_click,
#                hwndwrapper.delay_after_menuselect,
#                hwndwrapper.delay_after_sendkeys_key,
#                hwndwrapper.delay_after_button_click,
#                hwndwrapper.delay_before_after_close_click,
#            ), (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) )
#
#        set_timing(*prev_timing)

    def test_connect_path(self):
        """Test that connect_() works with a path"""
        app1 = Application()
        app1.start(_notepad_exe())

        app_conn = Application()
        app_conn.connect(path=self.notepad_subpath)
        self.assertEqual(app1.process, app_conn.process)

        app_conn = Application()
        if is_x64_Python() or not is_x64_OS():
            app_conn.connect(path=r"c:\windows\system32\notepad.exe")
        else:
            app_conn.connect(path=r"c:\windows\syswow64\notepad.exe")
        self.assertEqual(app1.process, app_conn.process)

        accessible_modules = process_get_modules()
        accessible_process_names = [os.path.basename(name.lower()) for process, name, cmdline in accessible_modules]
        self.assertEqual('notepad.exe' in accessible_process_names, True)

        app_conn.UntitledNotepad.menu_select('File->Exit')

    def test_connect_path_timeout(self):
        """Test that connect_() works with a path with timeout"""
        app1 = Application()
        def delayed_launch():
            time.sleep(2)
            app1.start(_notepad_exe())
        thread = Thread(target=delayed_launch)
        thread.start()

        app_conn = Application()
        app_conn.connect(path=_notepad_exe(), timeout=3)

        self.assertEqual(app1.process, app_conn.process)

        accessible_modules = process_get_modules()
        accessible_process_names = [os.path.basename(name.lower()) for process, name, cmdline in accessible_modules]
        self.assertEqual('notepad.exe' in accessible_process_names, True)

        app1.UntitledNotepad.menu_select('File->Exit')

    def test_connect_path_timeout_problem(self):
        """Test that connect_() raise error when no process start"""
        app1 = Application()
        def delayed_launch():
            time.sleep(1)
            app1.start(_notepad_exe())
        thread = Thread(target=delayed_launch)
        thread.start()

        self.assertRaises(ProcessNotFoundError, Application().connect, path=_notepad_exe(), timeout=0.5)

        time.sleep(0.7)

        app1.UntitledNotepad.menu_select('File->Exit')

    def test_connect_process_timeout_failed(self):
        """Test that connect_(pid=...) raise error when set timeout"""
        app1 = Application()
        app1.start(_notepad_exe())
        self.assertRaises(ProcessNotFoundError, Application().connect, pid=0, timeout=0.5)
        app1.UntitledNotepad.menu_select('File->Exit')

#    def test_Connect(self):
#        """Test that connect_() works with a path"""
#        app1 = Application()
#        app1.start_("notepad.exe")
#
#        app_conn = Application()
#        app_conn.connect_(path = r"system32\notepad.exe")
#        self.assertEqual(app1.process, app_conn.process)
#
#        app_conn = Application()
#        app_conn.connect_(path = r"c:\windows\system32\notepad.exe")
#        self.assertEqual(app1.process, app_conn.process)
#
#        app_conn.UntitledNotepad.menu_select('File->Exit')

    def test_connect_process(self):
        """Test that connect_() works with a process"""
        app1 = Application()
        app1.start(_notepad_exe())

        app_conn = Application()
        app_conn.connect(pid=app1.process)
        self.assertEqual(app1.process, app_conn.process)

        app_conn.UntitledNotepad.menu_select('File->Exit')


    def test_connect_handle(self):
        """Test that connect_() works with a handle"""
        app1 = Application()
        app1.start(_notepad_exe())
        handle = app1.UntitledNotepad.handle

        app_conn = Application()
        app_conn.connect(handle=handle)
        self.assertEqual(app1.process, app_conn.process)

        app_conn.UntitledNotepad.menu_select('File->Exit')

    def test_connect_windowspec(self):
        """Test that connect_() works with a windowspec"""
        app1 = Application()
        app1.start(_notepad_exe())
        #unused var: handle = app1.UntitledNotepad.handle

        app_conn = Application()
        try:
            app_conn.connect(name="Untitled - Notepad")
        except findwindows.WindowAmbiguousError:
            wins = findwindows.find_elements(active_only=True, name="Untitled - Notepad")
            app_conn.connect(handle = wins[0].handle)
        except findwindows.ElementNotFoundError:
            WaitUntil(30, 0.5, lambda: len(findwindows.find_elements(active_only=True, name="Untitled - Notepad")) > 0)
            wins = findwindows.find_elements(active_only=True, name="Untitled - Notepad")
            app_conn.connect(handle = wins[0].handle)

        self.assertEqual(app1.process, app_conn.process)

        app_conn.UntitledNotepad.menu_select('File->Exit')

    def test_connect_raises(self):
        """Test that connect_() raises with invalid input"""
        # try an argument that does not exist
        self.assertRaises (
            KeyError,
            Application().connect, **{'not_arg': 23})

        self.assertRaises (
            RuntimeError,
            Application().connect)

        # try to pass an invalid process
        self.assertRaises (
            ProcessNotFoundError,
            Application().connect, **{'pid': 0})

        # try to pass an invalid handle
        self.assertRaises(
            RuntimeError,
            Application().connect, **{'handle' : 0})

        # try to pass an invalid path
        self.assertRaises(
            ProcessNotFoundError,
            Application().connect, **{'path': "no app here", 'timeout': 0.0})

    def test_top_window(self):
        """Test that top_window_() works correctly"""
        Timings.window_find_timeout = 5
        app = Application()
        self.assertRaises(AppNotConnected, app.top_window_)

        app.start(_notepad_exe())

        self.assertEqual(app.UntitledNotepad.handle, app.top_window_().handle)

        app.UntitledNotepad.menu_select("Help->About Notepad")

        self.assertEqual(app.AboutNotepad.handle, app.top_window_().handle)

        app.AboutNotepad.Ok.Click()
        app.UntitledNotepad.menu_select("File->Exit")
        app.UntitledNotepad.wait_not('exists')
        self.assertRaises(RuntimeError, app.top_window_)

    def test_active_window(self):
        """Test that active_() works correctly"""
        app = Application()
        self.assertRaises(AppNotConnected, app.active_)
        self.assertRaises(AppNotConnected, app.is64bit)
        app.start(_notepad_exe())
        app.UntitledNotepad.wait('ready')
        self.assertEqual(app.active_().handle, app.UntitledNotepad.handle)
        app.UntitledNotepad.menu_select("File->Exit")
        app.UntitledNotepad.wait_not('exists')
        self.assertRaises(RuntimeError, app.active_)

    def test_cpu_usage(self):
        """Verify that cpu_usage() works correctly"""
        app = Application()
        self.assertRaises(AppNotConnected, app.cpu_usage)
        app.start(_notepad_exe())
        self.assertEqual(0.0 <= app.cpu_usage() <= 100.0, True)
        app.UntitledNotepad.menu_select("File->Exit")
        app.UntitledNotepad.wait_not('exists')

    def test_wait_cpu_usage_lower(self):
        """Test that wait_cpu_usage_lower() works correctly"""
        if is_x64_Python() != is_x64_OS():
            return None

        Application().Start(r'explorer.exe')

        def _cabinetwclass_exist():
            "Verify if at least one active 'CabinetWClass' window is created"
            l = findwindows.find_elements(active_only = True, class_name = 'CabinetWClass')
            return (len(l) > 0)

        WaitUntil(40, 0.5, _cabinetwclass_exist)
        handle = findwindows.find_elements(active_only = True, class_name = 'CabinetWClass')[-1].handle
        window = WindowSpecification({'handle': handle, 'backend': 'win32', })
        explorer = Application().Connect(pid = window.process_id())

        try:
            explorer.WaitCPUUsageLower(threshold = 1.5, timeout = 60, usage_interval = 2)
            window.AddressBandRoot.ClickInput()
            window.TypeKeys(r'Control Panel\Programs\Programs and Features', with_spaces=True, set_foreground=True)
            window.TypeKeys(r'{ENTER}', set_foreground = False)
            WaitUntil(40, 0.5, lambda: len(findwindows.find_elements(active_only=True,
                                                                     name='Programs and Features',
                                                                     class_name='CabinetWClass')) > 0)
            explorer.WaitCPUUsageLower(threshold = 1.5, timeout = 60, usage_interval = 2)
            installed_programs = window.FolderView.texts()[1:]
            programs_list = ','.join(installed_programs)
            if ('Microsoft' not in programs_list) and ('Python' not in programs_list):
                hwndwrapper.ImageGrab.grab().save(r'explorer_screenshot.jpg')
                hwndwrapper.ActionLogger().log('\ninstalled_programs:\n')
                for prog in installed_programs:
                    hwndwrapper.ActionLogger().log(prog)
            self.assertEqual(('Microsoft' in programs_list) or ('Python' in programs_list), True)
        finally:
            window.Close(2.0)

    if UIA_support:
        def test_wait_cpu_usage_lower_uia(self):
            """Test that wait_cpu_usage_lower() works correctly for UIA"""
            app = Application(backend='uia')
            app.start('notepad.exe')
            try:
                app.wait_cpu_usage_lower(threshold = 1.5, timeout = 30, usage_interval = 2)
            finally:
                app.kill()
            app.cpu_usage = mock.Mock(return_value=10)
            self.assertRaises(
                RuntimeError, app.wait_cpu_usage_lower,
                threshold = 9.0, timeout = 5, usage_interval = 0.5
                )

    # def test_wait_for_idle_exception(self):
    #    """Test that method start() raises an exception when wait for idle failed"""
    #    app = Application()
    #    self.assertRaises(Exception, app.start, 'cmd.exe')
    #    # TODO: test and fix the case when cmd.exe can't be killed by app.kill()

    def test_windows(self):
        """Test that windows_() works correctly"""
        Timings.window_find_timeout = 5
        app = Application()

        self.assertRaises(AppNotConnected, app.windows_, **{'title' : 'not connected'})

        app.start('notepad.exe')

        self.assertRaises(ValueError, app.windows_, **{'backend' : 'uia'})

        notepad_handle = app.UntitledNotepad.handle
        self.assertEqual(app.windows(visible=True), [notepad_handle])

        app.UntitledNotepad.menu_select("Help->About Notepad")

        aboutnotepad_handle = app.AboutNotepad.handle
        self.assertEqual(
            app.windows(visible=True, enabled=None),
            [aboutnotepad_handle, notepad_handle])

        app.AboutNotepad.OK.Click()
        app.UntitledNotepad.menu_select("File->Exit")

    def test_window(self):
        """Test that window_() works correctly"""
        app = Application()

        self.assertRaises(AppNotConnected, app.window_, **{'title' : 'not connected'})

        app.start(_notepad_exe())

        self.assertRaises(ValueError, app.windows_, **{'backend' : 'uia'})

        title = app.window(name="Untitled - Notepad")
        title_re = app.window(name_re="Untitled[ -]+Notepad")
        classname = app.window(class_name="Notepad")
        classname_re = app.window(class_name_re="Not..ad")
        handle = app.window(handle=title.handle)
        bestmatch = app.window(best_match="Untiotled Notepad")

        self.assertNotEqual(title.handle, None)
        self.assertNotEqual(title.handle, 0)

        self.assertEqual(title.handle, title_re.handle)
        self.assertEqual(title.handle, classname.handle)
        self.assertEqual(title.handle, classname_re.handle)
        self.assertEqual(title.handle, handle.handle)
        self.assertEqual(title.handle, bestmatch.handle)

        app.UntitledNotepad.menu_select("File->Exit")

    def test_getitem(self):
        """Test that __getitem__() works correctly"""
        Timings.window_find_timeout = 5
        app = Application()
        app.start(_notepad_exe())

        self.assertRaises(Exception, app['blahblah'])

        self.assertRaises(
            findbestmatch.MatchError,
            app['blahblah']['not here'].__getitem__, 'handle')

        self.assertEqual(
            app[u'Unt\xeftledNotepad'].handle,
            app.window(name="Untitled - Notepad").handle)

        app.UntitledNotepad.menu_select("Help->About Notepad")

        self.assertEqual(
            app['AboutNotepad'].handle,
            app.window(name="About Notepad").handle)

        app.AboutNotepad.Ok.Click()
        app.UntitledNotepad.menu_select("File->Exit")

    def test_getattribute(self):
        """Test that __getattribute__() works correctly"""
        Timings.window_find_timeout = 5
        app = Application()
        app.start(_notepad_exe())

        self.assertRaises(
            findbestmatch.MatchError,
            app.blahblah.__getattribute__, 'handle')

        self.assertEqual(
            app.UntitledNotepad.handle,
            app.window(name="Untitled - Notepad").handle)

        app.UntitledNotepad.menu_select("Help->About Notepad")

        # I think it's OK that this no longer raises a matcherror
        # just because the window is not enabled - doesn't mean you
        # should not be able to access it at all!
        #self.assertRaises(findbestmatch.MatchError,
        #    app.Notepad.__getattribute__, 'handle')

        self.assertEqual(
            app.AboutNotepad.handle,
            app.window(name="About Notepad").handle)

        app.AboutNotepad.Ok.Click()
        app.UntitledNotepad.menu_select("File->Exit")

    def test_kill(self):
        """test killing the application"""
        app = Application()
        app.start(_notepad_exe())

        app.UntitledNotepad.Edit.type_keys("hello")

        app.UntitledNotepad.menu_select("File->Print...")

        #app.Print.FindPrinter.Click() # Vasily: (Win7 x64) "Find Printer" dialog is from splwow64.exe process
        #app.FindPrinters.Stop.Click()

        app.kill()

        self.assertRaises(AttributeError, app.UntitledNotepad.Edit)

    def test_process_is_running(self):
        """Tests process is running and wait for exit function"""
        app = Application()
        app.start(_notepad_exe())
        app.UntitledNotepad.wait("ready")
        self.assertTrue(app.is_process_running())
        self.assertRaises(TimeoutError, lambda: app.wait_for_process_exit(timeout=5, retry_interval=1))
        app.kill()
        app.wait_for_process_exit()
        self.assertFalse(app.is_process_running())

    def test_should_return_not_running_if_not_started(self):
        """Tests that works on new instance

        is_process_running/wait_for_process_exit can be called on not started/disconnected instance
        """
        app = Application()
        app.wait_for_process_exit(timeout=10, retry_interval=1)
        self.assertFalse(app.is_process_running())

    class TestInheritedApp(Application):

        """Our inherited version of class"""

        def test_method(self):
            """This method should be called without any issues"""
            return self is not None

    def test_application_inheritance(self):
        """Test that Application class can be inherited and has it's own methods"""
        app = ApplicationTestCases.TestInheritedApp()
        self.assertTrue(app.test_method())

    def test_non_magic_application(self):
        app = Application()
        self.assertEqual(app.allow_magic_lookup, True)

        app_no_magic = Application(allow_magic_lookup=False)
        self.assertEqual(app_no_magic.allow_magic_lookup, False)

        app_no_magic.start(_notepad_exe())

        window = app_no_magic.window(best_match="UntitledNotepad")

        dlg = window.by(best_match="Edit")
        dlg.draw_outline()

        with self.assertRaises(AttributeError):
            app_no_magic.UntitledNotepad

        with self.assertRaises(AttributeError):
            window.Edit

        app_no_magic.kill()
        app_no_magic.wait_for_process_exit()

class WindowSpecificationTestCases(unittest.TestCase):

    """Unit tests for the application.Application class"""

    def setUp(self):
        """Set some data and ensure the application is in the state we want"""
        Timings.defaults()
        self.app = Application(backend="win32").start(_notepad_exe())
        self.dlgspec = self.app.UntitledNotepad
        self.ctrlspec = self.app.UntitledNotepad.Edit


    def tearDown(self):
        """Close the application after tests"""
        # close the application
        #self.app.UntitledNotepad.menu_select("File->Exit")
        self.app.kill()

    def test__init__(self):
        """Test creating a new spec by hand"""
        wspec = WindowSpecification(
            dict(
                best_match=u"UntitledNotepad",
                app=self.app)
            )

        self.assertEqual(
            wspec.window_text(),
            u"Untitled - Notepad")

        self.assertEqual(self.dlgspec.app, self.app)
        self.assertEqual(self.ctrlspec.app, self.app)
        self.assertEqual(wspec.app, self.app)

    def test__init__both_keywords(self):
        """Test creating a new spec with ambiguity by process and app simultaneously"""
        self.assertRaises(KeyError, WindowSpecification,
            dict(best_match=u"UntitledNotepad", app=self.app, pid=self.app.process)
            )

    def test__call__(self):
        """Test that __call__() correctly raises an error"""
        self.assertRaises(AttributeError, self.dlgspec)
        self.assertRaises(AttributeError, self.ctrlspec)

        # no best_match!
        wspec = WindowSpecification(
            dict(name=u"blah", app=self.app)
            )

        self.assertRaises(AttributeError, wspec)

    def test_wrapper_object(self):
        """Test that we can get a control"""
        self.assertEqual(True, isinstance(self.dlgspec, WindowSpecification))

        self.assertEqual(
            True,
            isinstance(self.dlgspec.find(), hwndwrapper.HwndWrapper)
            )

    def test_window(self):
        """test specifying a sub window of an existing specification"""
        sub_spec = self.dlgspec.by(class_name ="Edit")
        sub_spec_legacy = self.dlgspec.window(class_name = "Edit")

        self.assertEqual(True, isinstance(sub_spec, WindowSpecification))
        self.assertEqual(sub_spec.class_name(), "Edit")
        self.assertEqual(sub_spec_legacy.class_name(), "Edit")

    def test__getitem__(self):
        """test item access of a windowspec"""
        self.assertEqual(
            True,
            isinstance(self.dlgspec['Edit'], WindowSpecification)
            )

        self.assertEqual(self.dlgspec['Edit'].class_name(), "Edit")

        self.assertRaises(AttributeError, self.ctrlspec.__getitem__, 'edit')

    def test_getattr(self):
        """Test getting attributes works correctly"""
        self.assertEqual(
            True,
            isinstance(self.dlgspec.Edit, WindowSpecification)
            )

        self.assertEqual(self.dlgspec.Edit.class_name(), "Edit")

        # check that getting a dialog attribute works correctly
        self.assertEqual(
            "Notepad",
            self.dlgspec.class_name())

        # Check handling 'parent' as a WindowSpecification
        spec = self.ctrlspec.by(parent=self.dlgspec, visible=True)
        self.assertEqual(spec.class_name(), "Edit")

    def test_non_magic_getattr(self):
        ws = WindowSpecification(dict(best_match="Notepad"))
        self.assertEqual(ws.allow_magic_lookup, True)
        ws_no_magic = WindowSpecification(dict(best_match="Notepad"), allow_magic_lookup=False)
        self.assertEqual(ws_no_magic.allow_magic_lookup, False)

        dlg = ws_no_magic.by(best_match="Edit")
        has_focus = dlg.has_keyboard_focus()
        self.assertIn(has_focus, (True, False))

        with self.assertRaises(AttributeError):
            ws_no_magic.Edit


    def test_exists(self):
        """Check that windows exist"""
        self.assertEqual(True, self.dlgspec.exists())
        self.assertEqual(True, self.dlgspec.exists(0))
        self.assertEqual(True, self.ctrlspec.exists())
        # TODO: test a control that is not visible but exists
        #self.assertEqual(True, self.app.DefaultIME.exists())

        start = timestamp()
        self.assertEqual(False, self.app.BlahBlah.exists(timeout=.1))
        self.assertEqual(True, timestamp() - start < .3)

        start = timestamp()
        self.assertEqual(False, self.app.BlahBlah.exists(timeout=3))
        self.assertEqual(True, 2.7 < timestamp() - start < 3.3)

    def test_exists_timing(self):
        """test the timing of the exists method"""
        # try ones that should be found immediately
        start = timestamp()
        self.assertEqual(True, self.dlgspec.exists())
        self.assertEqual(True, timestamp() - start < .3)

        start = timestamp()
        self.assertEqual(True, self.ctrlspec.exists())
        self.assertEqual(True, timestamp() - start < .3)

        # try one that should not be found
        start = timestamp()
        self.assertEqual(True, self.dlgspec.exists(.5))
        timedif = timestamp() - start
        self.assertEqual(True, .49 > timedif < .6)

    def test_find_all_dlg(self):
        dlg_spec_list = self.dlgspec.find_all()
        self.assertEqual(1, len(dlg_spec_list))
        self.assertEqual(self.dlgspec.find(), dlg_spec_list[0])

    def test_find_all_notepad(self):
        ctrls = self.dlgspec.by(parent=self.dlgspec).find_all()

        self.assertEqual(2, len(ctrls))
        self.assertEqual(ctrls[0], self.app.Notepad.Edit.find())
        self.assertEqual(ctrls[1], self.app.Notepad.StatusBar.find())

    def test_wait(self):
        """test the functionality and timing of the wait method"""
        allowable_error = .2

        start = timestamp()
        self.assertEqual(self.dlgspec.find(), self.dlgspec.wait("enaBleD "))
        time_taken = (timestamp() - start)
        if not 0 <= time_taken < (0 + 2 * allowable_error):
            self.assertEqual(.02,  time_taken)

        start = timestamp()
        self.assertEqual(self.dlgspec.find(), self.dlgspec.wait("  ready"))
        self.assertEqual(True, 0 <= (timestamp() - start) < 0 + allowable_error)

        start = timestamp()
        self.assertEqual(self.dlgspec.find(), self.dlgspec.wait(" exiSTS"))
        self.assertEqual(True, 0 <= (timestamp() - start) < 0 + allowable_error)

        start = timestamp()
        self.assertEqual(self.dlgspec.find(), self.dlgspec.wait(" VISIBLE "))
        self.assertEqual(True, 0 <= (timestamp() - start) < 0 + allowable_error)

        start = timestamp()
        self.assertEqual(self.dlgspec.find(), self.dlgspec.wait(" ready enabled"))
        self.assertEqual(True, 0 <= (timestamp() - start) < 0 + allowable_error)

        start = timestamp()
        self.assertEqual(self.dlgspec.find(), self.dlgspec.wait("visible exists "))
        self.assertEqual(True, 0 <= (timestamp() - start) < 0 + allowable_error)

        start = timestamp()
        self.assertEqual(self.dlgspec.find(), self.dlgspec.wait("exists "))
        self.assertEqual(True, 0 <= (timestamp() - start) < 0 + allowable_error)

        start = timestamp()
        self.assertEqual(self.dlgspec.find(), self.dlgspec.wait("actIve "))
        self.assertEqual(True, 0 <= (timestamp() - start) < 0 + allowable_error)

        self.assertRaises(SyntaxError, self.dlgspec.wait, "Invalid_criteria")

    def test_wait_non_existing(self):
        """test timing of the wait method for non-existing element"""
        allowable_error = .2

        start = timestamp()
        self.assertRaises(TimeoutError, self.app.BlahBlah.wait, 'exists')
        expected = Timings.window_find_timeout
        self.assertEqual(True, expected - allowable_error <= (timestamp() - start) < expected + allowable_error)

    def test_wait_invisible(self):
        """test timing of the wait method for non-existing element and existing invisible one"""
        # TODO: re-use an MFC sample for this test
        allowable_error = .2

        start = timestamp()
        self.assertRaises(TimeoutError, self.app.BlahBlah.wait, 'visible')
        expected = Timings.window_find_timeout
        self.assertEqual(True, expected - allowable_error <= (timestamp() - start) < expected + allowable_error)

        # make sure Status Bar is not visible
        status_bar_menu = self.app.UntitledNotepad.menu().item('&View').sub_menu().item('&Status Bar')
        if status_bar_menu.is_checked():
            status_bar_menu.select()

        # check that existing invisible control is still found with 'exists' criterion
        status_bar_spec = self.app.UntitledNotepad.by(class_name="msctls_statusbar32", visible=None)
        self.assertEqual('StatusBar', status_bar_spec.wait('exists').friendly_class_name())

        start = timestamp()
        self.assertRaises(TimeoutError, status_bar_spec.wait, 'exists visible')
        self.assertEqual(True, expected - allowable_error <= (timestamp() - start) < expected + allowable_error)

        start = timestamp()
        self.assertRaises(TimeoutError, status_bar_spec.wait, 'visible exists')
        self.assertEqual(True, expected - allowable_error <= (timestamp() - start) < expected + allowable_error)

    def test_wait_not(self):
        """
        Test that wait not fails for all the following

        * raises and error when criteria not met
        * timing is close to the timeout value
        """
        allowable_error = .16

        start = timestamp()
        self.assertRaises(TimeoutError, self.dlgspec.wait_not, "enaBleD ", .1, .05)
        taken = timestamp() - start
        if .1 < (taken)  > .1 + allowable_error:
            self.assertEqual(.12, taken)

        start = timestamp()
        self.assertRaises(TimeoutError, self.dlgspec.wait_not, "  ready", .1, .05)
        self.assertEqual(True, .1 <= (timestamp() - start) < .1 + allowable_error)

        start = timestamp()
        self.assertRaises(TimeoutError, self.dlgspec.wait_not, " exiSTS", .1, .05)
        self.assertEqual(True, .1 <= (timestamp() - start) < .1 + allowable_error)

        start = timestamp()
        self.assertRaises(TimeoutError, self.dlgspec.wait_not, " VISIBLE ", .1, .05)
        self.assertEqual(True, .1 <= (timestamp() - start) < .1 + allowable_error)

        start = timestamp()
        self.assertRaises(TimeoutError, self.dlgspec.wait_not, " ready enabled", .1, .05)
        self.assertEqual(True, .1 <= (timestamp() - start) < .1 + allowable_error)

        start = timestamp()
        self.assertRaises(TimeoutError, self.dlgspec.wait_not, "visible exists ", .1, .05)
        self.assertEqual(True, .1 <= (timestamp() - start) < .1 + allowable_error)

        start = timestamp()
        self.assertRaises(TimeoutError, self.dlgspec.wait_not, "exists ", .1, .05)
        self.assertEqual(True, .1 <= (timestamp() - start) < .1 + allowable_error)

        start = timestamp()
        self.assertRaises(TimeoutError, self.dlgspec.wait_not, "actIve ", .1, .05)
        self.assertEqual(True, .1 <= (timestamp() - start) < .1 + allowable_error)

        self.assertRaises(SyntaxError, self.dlgspec.wait_not, "Invalid_criteria")

#    def test_wait_ready(self):
#        """Make sure the friendly class is set correctly"""
#        allowable_error = .02
#
#        start = timestamp()
#        self.assertEqual(self.dlgspec.ctrl_(), self.dlgspec.WaitReady(.1, .05))
#
#        # it it didn't finish in the allocated time then raise an error
#        # we assertEqual to something that we know is not right - to get a
#        # better error report
#        if not 0 <= (timestamp() - start) < 0 + allowable_error:
#            self.assertEqual(0, timestamp() - start)
#        #self.assertEqual(True, 0 <= (timestamp() - start) < 0 + allowable_error)
#
#
#    def testWaitNotReady(self):
#        "Make sure the friendly class is set correctly"
#
#        allowable_error = .02
#
#        start = timestamp()
#        self.assertRaises(RuntimeError, self.dlgspec.WaitNotReady, .1, .05)
#
#        if not .1 <= (timestamp() - start) < .1 + allowable_error:
#            self.assertEqual(.1, timestamp() - start)
#
#        #self.assertEqual(True, .1 <= (timestamp() - start) < .1 + allowable_error)
#
#
#    def testWaitEnabled(self):
#        "Make sure the friendly class is set correctly"
#
#        allowable_error = .02
#
#        start = timestamp()
#        self.assertEqual(self.dlgspec.ctrl_(), self.dlgspec.WaitEnabled(.1, .05))
#
#        if not 0 <= (timestamp() - start) < 0 + allowable_error:
#            self.assertEqual(0, timestamp() - start)
#
#        #self.assertEqual(True, 0 <= (timestamp() - start) < 0 + allowable_error)
#
#
#    def testWaitNotEnabled(self):
#        "Make sure the friendly class is set correctly"
#
#        allowable_error = .02
#
#        start = timestamp()
#        self.assertRaises(RuntimeError, self.dlgspec.WaitNotEnabled, .1, .05)
#        if not .1 <= (timestamp() - start) < .1 + allowable_error:
#            self.assertEqual(.1, timestamp() - start)
#        #self.assertEqual(True, .1 <= (timestamp() - start) < .1 + allowable_error)
#
#    def testWaitVisible(self):
#        "Make sure the friendly class is set correctly"
#
#        allowable_error = .02
#
#        start = timestamp()
#        self.assertEqual(self.dlgspec.ctrl_(), self.dlgspec.WaitVisible(.1, .05))
#        if not 0 <= (timestamp() - start) < 0 + allowable_error:
#            self.assertEqual(0, timestamp() - start)
#        #self.assertEqual(True, 0 <= (timestamp() - start) < 0 + allowable_error)
#
#    def testWaitNotVisible(self):
#        "Make sure the friendly class is set correctly"
#
#        allowable_error = .02
#
#        start = timestamp()
#        self.assertRaises(RuntimeError, self.dlgspec.WaitNotVisible, .1, .05)
#        # it it didn't finish in the allocated time then raise an error
#        # we assertEqual to something that we know is not right - to get a
#        # better error report
#        if not .1 <= (timestamp() - start) < .1 + allowable_error:
#            self.assertEqual(.1, timestamp() - start)
#
#    def testWaitExists(self):
#        "Make sure the friendly class is set correctly"
#
#        allowable_error = .02
#
#        start = timestamp()
#        self.assertEqual(self.dlgspec.ctrl_(), self.dlgspec.WaitExists(.1, .05))
#
#        # it it didn't finish in the allocated time then raise an error
#        # we assertEqual to something that we know is not right - to get a
#        # better error report
#        if not 0 <= (timestamp() - start) < 0 + allowable_error:
#            self.assertEqual(.1, timestamp() - start)
#
#    def testWaitNotExists(self):
#        "Make sure the friendly class is set correctly"
#
#        allowable_error = .02
#
#        start = timestamp()
#        self.assertRaises(RuntimeError, self.dlgspec.WaitNotExists, .1, .05)
#        if not .1 <= (timestamp() - start) < .1 + allowable_error:
#            self.assertEqual(.1, timestamp() - start)
#        #self.assertEqual(True, .1 <= (timestamp() - start) < .1 + allowable_error)

    def test_depth(self):
        """Test that descendants() with depth works correctly"""
        self.dlgspec.menu_select("Format -> Font")

        self.assertNotEqual(
            len(self.app['Font'].descendants(depth=1)),
            len(self.app['Font'].descendants(depth=2)))

    def test_dump_tree(self):
        """Make sure dump_tree() doesn't crash"""
        self.dlgspec.dump_tree()
        self.ctrlspec.dump_tree()

    def test_dump_tree_file_output(self):
        """Make sure dump_tree() creates correct file"""
        output_filename = "test_dump_tree.txt"
        self.dlgspec.dump_tree(filename=output_filename)
        if os.path.isfile(output_filename):
            with open(output_filename, "r") as test_log_file:
                content = str(test_log_file.readlines())
                self.assertTrue("'Untitled - NotepadEdit'" in content
                    and "'Edit'" in content)
                self.assertTrue(".by(class_name='msctls_statusbar32'" in content)
            os.remove(output_filename)
        else:
            self.fail("dump_tree can't create a file")

        self.ctrlspec.dump_tree(filename=output_filename)
        if os.path.isfile(output_filename):
            with open(output_filename, "r") as test_log_file:
                content = str(test_log_file.readlines())
                self.assertTrue(".by(class_name='Edit')" in content)
            os.remove(output_filename)
        else:
            self.fail("dump_tree can't create a file")

    def test_find_elements_re(self):
        """Test for bug #90: A crash in 'find_elements' when called with 'title_re' argument"""
        self.dlgspec.wait('visible')
        windows = findwindows.find_elements(name_re="Untitled - Notepad")
        self.assertTrue(len(windows) >= 1)


class ChildWindowSpecificationFromWrapperTests(unittest.TestCase):
    def setUp(self):
        """Set some data and ensure the application is in the state we want"""
        Timings.defaults()
        self.app = Application(backend="win32").start(_notepad_exe())
        self.ctrlspec = self.app.window(found_index=0).find().by(class_name='Edit')

    def tearDown(self):
        """Close the application after tests"""
        self.app.kill()
        
    def test_wrapper_object(self):
        """Test that we can get a control"""
        self.assertEqual(True, isinstance(self.ctrlspec, WindowSpecification))

        self.assertEqual(
            True,
            isinstance(self.ctrlspec.find(), hwndwrapper.HwndWrapper)
            )

    def test_parent(self):
        """Test recreating specification from parent dialog wrapper"""
        dlg = self.ctrlspec.parent()
        sub_spec = dlg.by(class_name ="Edit")

        self.assertEqual(True, isinstance(sub_spec, WindowSpecification))
        self.assertEqual(sub_spec.class_name(), "Edit")
        self.assertEqual(self.ctrlspec.handle, sub_spec.handle)

    def test_dump_tree_file_output(self):
        """Make sure dump_tree() creates correct file"""
        output_filename = "test_dump_tree.txt"

        self.ctrlspec.dump_tree(filename=output_filename)
        if os.path.isfile(output_filename):
            with open(output_filename, "r") as test_log_file:
                content = str(test_log_file.readlines())
                self.assertTrue(".by(class_name='Edit')" in content)
            os.remove(output_filename)
        else:
            self.fail("dump_tree can't create a file")

    def test_properties(self):
        """Check control properties"""
        self.assertEqual(self.ctrlspec.class_name(), "Edit")
        self.assertTrue(self.ctrlspec.exists())


if UIA_support:
    class UIAWindowSpecificationTestCases(unittest.TestCase):
        """Unit tests for the application.Application class with UIA backend"""

        def setUp(self):
            """Set some data and ensure the application is in the state we want"""
            Timings.defaults()
            self.app = Application(backend="uia").start(_notepad_exe())
            self.dlgspec = self.app.UntitledNotepad

        def tearDown(self):
            """Close the application after tests"""
            self.app.kill()

        def test_child_window_depth(self):
            """Test that child_window() with depth works correctly"""
            # TODO fix same elements at different tree levels on win32 backend
            self.dlgspec.menu_select("Format -> Font")
            font = self.dlgspec.by(name="Font")

            with self.assertRaises(findbestmatch.MatchError):
                font.by(best_match="ListBox0", depth=1).find()
            font.by(best_match="ListBox0", depth=2).find()

class WaitUntilDecoratorTests(unittest.TestCase):
    """Unit tests for always_wait_until and always_wait_until_passes decorators"""

    def test_always_wait_until_decorator_success(self):
        """Test always_wait_until_decorator success"""

        @always_wait_until(4, 2)
        def foo():
            return True
        self.assertTrue(foo())

    def test_always_wait_until_decorator_failure(self):
        """Test wait_until_decorator failure"""

        @always_wait_until(4, 2)
        def foo():
            return False
        self.assertRaises(TimeoutError, foo)

    def test_always_wait_until_passes_decorator_success(self):
        """Test always_wait_until_passes_decorator success"""

        @always_wait_until_passes(4, 2)
        def foo():
            return True
        self.assertTrue(foo())

    def test_always_wait_until_passes_decorator_failure(self):
        """Test always_wait_until_passes_decorator failure"""

        @always_wait_until_passes(4, 2)
        def foo():
            raise Exception("Unexpected Error in foo")
        self.assertRaises(TimeoutError, foo)


class MultiLevelWindowSpecificationTests(unittest.TestCase):

    """Unit tests for multi-level (3+) WindowSpecification objects"""

    if UIA_support:
        def setUp(self):
            """Set some data and ensure the application is in the state we want"""
            Timings.slow()
            self.app = Application(backend='uia').start(os.path.join(mfc_samples_folder, u"RowList.exe"))
            self.dlg = self.app.RowListSampleApplication

        def tearDown(self):
            """Close the application after tests"""
            self.dlg.CloseButton.click()
            self.dlg.wait_not('visible')

        def test_3level_specification(self):
            """Test that controls can be accessed by 3 levels of attributes"""
            self.dlg.Toolbar.About.click()
            self.dlg.AboutRowList.OK.click()
            #self.dlg.AboutRowList.wait_not('visible') # XXX: it takes more than 50 seconds!

    else: # Win32
        def setUp(self):
            """Set some data and ensure the application is in the state we want"""
            Timings.defaults()
            self.app = Application(backend='win32').start(os.path.join(mfc_samples_folder, u"CmnCtrl3.exe"))
            self.dlg = self.app.CommonControlsSample

        def tearDown(self):
            """Close the application after tests"""
            self.dlg.SendMessage(win32defines.WM_CLOSE)

        def test_4level_specification(self):
            """Test that controls can be accessed by 4 levels of attributes"""
            self.assertEqual(self.dlg.CPagerCtrl.Pager.Toolbar.button_count(), 12)


if UIA_support:
    class DesktopUiaWindowSpecificationTests(unittest.TestCase):

        """Unit tests for Desktop(backend='uia') object"""

        def setUp(self):
            """Set some data and ensure the application is in the state we want"""
            Timings.slow()
            self.app = Application().start('explorer.exe "' + mfc_samples_folder_32 + '"')
            self.desktop = Desktop(backend='uia')
            self.desktop_no_magic = Desktop(backend='uia', allow_magic_lookup=False)

        def tearDown(self):
            """Close the application after tests"""
            self.desktop.MFC_samplesDialog.close()
            self.desktop.MFC_samplesDialog.wait_not('exists')

        def test_folder_list(self):
            """Test that ListViewWrapper returns correct files list in explorer.exe"""
            files_list = self.desktop.MFC_samplesDialog.Shell_Folder_View.Items_View.find()
            self.assertEqual([item.window_text() for item in files_list.get_items()],
                             [u'x64', u'BCDialogMenu.exe', u'CmnCtrl1.exe', u'CmnCtrl2.exe', u'CmnCtrl3.exe',
                              u'CtrlTest.exe', u'mfc100u.dll', u'NewControls.exe', u'RebarTest.exe', u'RowList.exe', u'TrayMenu.exe'])
            self.assertEqual(files_list.item('RebarTest.exe').window_text(), 'RebarTest.exe')
        def test_set_backend_to_window_uia(self):
            """Set backend to method window(), except exception ValueError"""
            with self.assertRaises(ValueError):
                self.desktop.window(backend='uia', name='MFC_samplesDialog')
            with self.assertRaises(ValueError):
                self.desktop.window(backend='win32', name='MFC_samplesDialog')

        def test_get_list_of_windows_uia(self):
            """Test that method .windows() returns a non-empty list of windows"""
            dlgs = self.desktop.windows()
            self.assertTrue(len(dlgs) > 1)

        def test_set_backend_to_windows_uia(self):
            """Set backend to method .windows(), except exception ValueError"""
            with self.assertRaises(ValueError):
                self.desktop.windows(backend='win32')
            with self.assertRaises(ValueError):
                self.desktop.windows(backend='uia')

        def test_only_visible_windows_uia(self):
            """Set visible=True to method .windows()"""
            dlgs = self.desktop.windows(visible=True)
            self.assertTrue(all([win.is_visible() for win in dlgs]))

        def test_only_enable_windows_uia(self):
            """Set enable_only to the method windows"""
            dlgs = self.desktop.windows(enabled=True)
            self.assertTrue(all([win.is_enabled() for win in dlgs]))

        def test_non_magic_desktop(self):
            from pywinauto.controls.uiawrapper import UIAWrapper

            self.assertEqual(self.desktop.allow_magic_lookup, True)
            self.assertEqual(self.desktop_no_magic.allow_magic_lookup, False)

            dlgs = self.desktop_no_magic.windows()
            self.assertTrue(len(dlgs) > 1)

            window = self.desktop_no_magic.window(name="MFC_samples")
            self.assertEqual(window.allow_magic_lookup, False)

            dlg = window.by(class_name="ShellTabWindowClass").find()
            self.assertIsInstance(dlg, UIAWrapper)

            has_focus = dlg.has_keyboard_focus()
            self.assertIn(has_focus, (True, False))

            with self.assertRaises(AttributeError):
                self.desktop_no_magic.MFC_samples

            with self.assertRaises(AttributeError):
                window.ShellTabWindowClass


class DesktopWin32WindowSpecificationTests(unittest.TestCase):

    """Unit tests for Desktop(backend='win32') object"""

    def setUp(self):
        """Set some data and ensure the application is in the state we want"""
        Timings.defaults()
        self.app = Application(backend='win32').start(os.path.join(mfc_samples_folder, u"CmnCtrl3.exe"))
        self.desktop = Desktop(backend='win32')
        self.desktop_no_magic = Desktop(backend='win32', allow_magic_lookup=False)
        self.window_title = 'Common Controls Sample'

    def tearDown(self):
        """Close the application after tests"""
        self.desktop.window(name=self.window_title, pid=self.app.process).SendMessage(win32defines.WM_CLOSE)

    def test_simple_access_through_desktop(self):
        """Test that controls can be accessed by 4 levels of attributes"""
        dlg = self.desktop.window(name=self.window_title, pid=self.app.process)
        self.assertEqual(dlg.Pager.Toolbar.button_count(), 12)

    def test_set_backend_to_window_win32(self):
        """Set backend to method window(), except exception ValueError"""
        with self.assertRaises(ValueError):
            self.desktop.window(backend='uia', name=self.window_title, pid=self.app.process)
        with self.assertRaises(ValueError):
            self.desktop.window(backend='win32', name=self.window_title, pid=self.app.process)

    def test_get_list_of_windows_win32(self):
        """Test that method .windows() returns a non-empty list of windows"""
        dlgs = self.desktop.windows()

        self.assertTrue(len(dlgs) > 1)
        window_titles = [win_obj.window_text() for win_obj in dlgs]
        self.assertTrue(self.window_title in window_titles)

    def test_set_backend_to_windows_win32(self):
        """Set backend to method windows, except exception ValueError"""
        with self.assertRaises(ValueError):
            self.desktop.windows(backend='win32')
        with self.assertRaises(ValueError):
            self.desktop.windows(backend='uia')

    def test_only_visible_windows_win32(self):
        """Set visible=True to method .windows()"""
        dlgs = self.desktop.windows(visible=True)
        self.assertTrue(all([win.is_visible() for win in dlgs]))

    def test_only_enable_windows_win32(self):
        """Set enable_only to the method windows"""
        dlgs = self.desktop.windows(enabled=True)
        self.assertTrue(all([win.is_enabled() for win in dlgs]))

    def test_from_point_win32(self):
        """Test method Desktop(backend='win32').from_point(x, y)"""
        combo = self.app.Common_Controls_Sample.ComboBox.find()
        x, y = combo.rectangle().mid_point()
        combo_from_point = self.desktop.from_point(x, y)
        self.assertEqual(combo, combo_from_point)

    def test_top_from_point_win32(self):
        """Test method Desktop(backend='win32').top_from_point(x, y)"""
        combo = self.app.Common_Controls_Sample.ComboBox.find()
        dlg = self.app.Common_Controls_Sample.find()
        x, y = combo.rectangle().mid_point()
        dlg_from_point = self.desktop.top_from_point(x, y)
        self.assertEqual(dlg, dlg_from_point)

    def test_non_magic_desktop(self):
        self.assertEqual(self.desktop.allow_magic_lookup, True)
        self.assertEqual(self.desktop_no_magic.allow_magic_lookup, False)

        window = self.desktop_no_magic.window(name=self.window_title, pid=self.app.process)
        self.assertEqual(window.allow_magic_lookup, False)

        dlg = window.by(class_name="msctls_trackbar32").find()
        self.assertIsInstance(dlg, TrackbarWrapper)

        pos = dlg.get_position()
        self.assertIsInstance(pos, six.integer_types)

        with self.assertRaises(AttributeError):
            getattr(self.desktop_no_magic, self.window_title.replace(" ", "_"))

        with self.assertRaises(AttributeError):
            window.msctls_trackbar32


if __name__ == "__main__":
    unittest.main()
