示例#1
0
    def test_nearby_clipping(self):
        screen = AutoPyController()
        screen_width = screen.width
        screen_height = screen.height

        # clip upper side
        region = Region(200, 100, 20, 10).nearby(rrange=150)
        self.assertEqual(50, region.x)
        self.assertEqual(0, region.y)
        self.assertEqual(320, region.width)
        self.assertEqual(260, region.height)

        # clip lower side
        region = Region(200, screen_height - 30, 20, 10).nearby(rrange=50)
        self.assertEqual(150, region.x)
        self.assertEqual(screen_height - 30 - 50, region.y)
        self.assertEqual(120, region.width)
        self.assertEqual(80, region.height)

        # clip left side
        region = Region(20, 100, 30, 10).nearby(rrange=50)
        self.assertEqual(0, region.x)
        self.assertEqual(50, region.y)
        self.assertEqual(100, region.width)
        self.assertEqual(110, region.height)

        # clip right side
        region = Region(screen_width - 30, 100, 20, 10).nearby(rrange=50)
        self.assertEqual(screen_width - 30 - 50, region.x)
        self.assertEqual(50, region.y)
        self.assertEqual(80, region.width)
        self.assertEqual(110, region.height)
示例#2
0
    def test_left(self):
        region = Region(200, 100, 20, 10).left(50)
        self.assertEqual(150, region.x)
        self.assertEqual(100, region.y)
        self.assertEqual(70, region.width)
        self.assertEqual(10, region.height)

        region = Region(200, 100, 20, 10).left(80000)
        self.assertEqual(0, region.x)
        self.assertEqual(100, region.y)
        self.assertEqual(220, region.width)
        self.assertEqual(10, region.height)

        # extend to full screen above
        region = Region(200, 100, 20, 10).left()
        self.assertEqual(0, region.x)
        self.assertEqual(100, region.y)
        self.assertEqual(220, region.width)
        self.assertEqual(10, region.height)
示例#3
0
    def test_above(self):
        region = Region(200, 100, 20, 10).above(50)
        self.assertEqual(200, region.x)
        self.assertEqual(50, region.y)
        self.assertEqual(20, region.width)
        self.assertEqual(60, region.height)

        region = Region(200, 100, 20, 10).above(80000)
        self.assertEqual(200, region.x)
        self.assertEqual(0, region.y)
        self.assertEqual(20, region.width)
        self.assertEqual(110, region.height)

        # extend to full screen above
        region = Region(200, 100, 20, 10).above()
        self.assertEqual(200, region.x)
        self.assertEqual(0, region.y)
        self.assertEqual(20, region.width)
        self.assertEqual(110, region.height)
示例#4
0
    def setUp(self):
        # gui test scripts
        self.script_app = os.path.join(common_test.unittest_dir, 'qt5_application.py')
        self.child_app = None

        # prefixed controls
        # NOTE: provide and use only fixed locations to avoid CV backend dependencies
        self.click_control = Location(75, 25)
        self.double_click_control = Location(185, 20)
        self.context_menu_control = Location(315, 20)
        self.context_menu_close_control = Location(355, 35)
        self.mouse_down_control = Location(435, 95)
        self.mouse_up_control = Location(435, 135)
        self.textedit_control = Location(35, 135)
        self.textedit_quit_control = Location(65, 60)
        self.textedit_any_control = Location(65, 95)
        self.drag_control = Location(435, 25)
        self.drop_control = Location(435, 65)

        self.region = Region()
示例#5
0
    def test_screen_clipping(self):
        screen = AutoPyController()
        screen_width = screen.width
        screen_height = screen.height

        region = Region(0, 0, 80000, 40000)
        self.assertEqual(screen_width, region.width)
        self.assertEqual(screen_height, region.height)

        region = Region(80000, 40000, 300, 200)
        self.assertEqual(screen_width - 1, region.x)
        self.assertEqual(screen_height - 1, region.y)
        self.assertEqual(1, region.width)
        self.assertEqual(1, region.height)

        region = Region(200, 100, screen_width * 2, screen_height * 2)
        self.assertEqual(200, region.x)
        self.assertEqual(100, region.y)
        self.assertEqual(screen_width - region.x, region.width)
        self.assertEqual(screen_height - region.y, region.height)
示例#6
0
    def test_right(self):
        screen_width = AutoPyController().width

        region = Region(200, 100, 20, 10).right(50)
        self.assertEqual(200, region.x)
        self.assertEqual(100, region.y)
        self.assertEqual(70, region.width)
        self.assertEqual(10, region.height)

        region = Region(200, 100, 20, 10).right(80000)
        self.assertEqual(200, region.x)
        self.assertEqual(100, region.y)
        self.assertEqual(screen_width - region.x, region.width)
        self.assertEqual(10, region.height)

        # extend to full screen above
        region = Region(200, 100, 20, 10).right()
        self.assertEqual(200, region.x)
        self.assertEqual(100, region.y)
        self.assertEqual(screen_width - region.x, region.width)
        self.assertEqual(10, region.height)
示例#7
0
    def test_below(self):
        screen_height = AutoPyController().height

        region = Region(200, 100, 20, 10).below(50)
        self.assertEqual(200, region.x)
        self.assertEqual(100, region.y)
        self.assertEqual(20, region.width)
        self.assertEqual(60, region.height)

        region = Region(200, 100, 20, 10).below(80000)
        self.assertEqual(200, region.x)
        self.assertEqual(100, region.y)
        self.assertEqual(20, region.width)
        self.assertEqual(screen_height - region.y, region.height)

        # extend to full screen below
        region = Region(200, 100, 20, 10).below()
        self.assertEqual(200, region.x)
        self.assertEqual(100, region.y)
        self.assertEqual(20, region.width)
        self.assertEqual(screen_height - region.y, region.height)
示例#8
0
    def test_empty_screen_clipping(self):
        screen = Controller()
        screen_width = screen.width
        screen_height = screen.height
        self.assertEqual(screen_width, 0)
        self.assertEqual(screen_height, 0)

        region = Region(0, 0, 80000, 40000, dc=screen)
        self.assertEqual(region.width, 80000)
        self.assertEqual(region.height, 40000)

        region = Region(80000, 40000, 300, 200, dc=screen)
        self.assertEqual(region.x, 80000)
        self.assertEqual(region.y, 40000)
        self.assertEqual(region.width, 300)
        self.assertEqual(region.height, 200)

        region = Region(-300, -200, 300, 200, dc=screen)
        self.assertEqual(region.x, -300)
        self.assertEqual(region.y, -200)
        self.assertEqual(region.width, 300)
        self.assertEqual(region.height, 200)
示例#9
0
    def test_nearby(self):
        screen = AutoPyController()
        screen_width = screen.width
        screen_height = screen.height

        # defaults to 50 pixels
        region = Region(200, 100, 20, 10).nearby()
        self.assertEqual(150, region.x)
        self.assertEqual(50, region.y)
        self.assertEqual(120, region.width)
        self.assertEqual(110, region.height)

        region = Region(200, 100, 20, 10).nearby(rrange=80000)
        self.assertEqual(0, region.x)
        self.assertEqual(0, region.y)
        self.assertEqual(screen_width, region.width)
        self.assertEqual(screen_height, region.height)

        region = Region(200, 100, 20, 10).nearby(rrange=0)
        self.assertEqual(200, region.x)
        self.assertEqual(100, region.y)
        self.assertEqual(20, region.width)
        self.assertEqual(10, region.height)
示例#10
0
    def test_initialize(self):
        screen_width = AutoPyController().width
        screen_height = AutoPyController().height

        self.assertEqual(0, self.region.x)
        self.assertEqual(0, self.region.y)
        self.assertEqual(screen_width, self.region.width)
        self.assertEqual(screen_height, self.region.height)

        region = Region(10, 20, 300, 200)
        self.assertEqual(10, region.x)
        self.assertEqual(20, region.y)
        self.assertEqual(300, region.width)
        self.assertEqual(200, region.height)
示例#11
0
    def test_sample(self):
        self.show_image('all_shapes')

        # autopy matching does not support similarity
        shapes = Region(cv=AutoPyFinder())
        similarity = shapes.sample(Image('shape_blue_circle'))
        self.assertEqual(similarity, 0.0)

        # initialize template matching region to support similarity
        shapes = Region(cv=TemplateFinder())
        similarity = shapes.sample(Image('shape_blue_circle'))
        self.assertAlmostEqual(similarity, 0.999999, delta=0.001)

        self.close_windows()
示例#12
0
    def test_capture(self):
        for display in self.backends:
            screen_width = display.width
            screen_height = display.height

            # Fullscreen capture
            captured = display.capture_screen()
            self.assertEqual(screen_width, captured.width)
            self.assertEqual(screen_height, captured.height)

            # Capture with coordinates
            captured = display.capture_screen(20, 10, int(screen_width / 2),
                                              int(screen_height / 2))
            self.assertEqual(int(screen_width / 2), captured.width)
            self.assertEqual(int(screen_height / 2), captured.height)

            # Capture with Region
            region = Region(10, 10, 320, 200)
            captured = display.capture_screen(region)
            self.assertEqual(320, captured.width)
            self.assertEqual(200, captured.height)
示例#13
0
    def test_position_calc(self):
        region = Region(10, 20, 300, 200)

        center = region.center
        self.assertEqual(160, center.x)
        self.assertEqual(120, center.y)

        top_left = region.top_left
        self.assertEqual(10, top_left.x)
        self.assertEqual(20, top_left.y)

        top_right = region.top_right
        self.assertEqual(310, top_right.x)
        self.assertEqual(20, top_right.y)

        bottom_left = region.bottom_left
        self.assertEqual(10, bottom_left.x)
        self.assertEqual(220, bottom_left.y)

        bottom_right = region.bottom_right
        self.assertEqual(310, bottom_right.x)
        self.assertEqual(220, bottom_right.y)
示例#14
0
class RegionTest(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.file_resolver = FileResolver()
        cls.file_resolver.add_path(os.path.join(common_test.unittest_dir, 'images'))

        # preserve values of static attributes
        cls.prev_loglevel = GlobalConfig.image_logging_level
        cls.prev_logpath = GlobalConfig.image_logging_destination
        GlobalConfig.image_logging_level = 0
        GlobalConfig.image_logging_destination = os.path.join(common_test.unittest_dir, 'tmp')

    @classmethod
    def tearDownClass(cls):
        GlobalConfig.image_logging_level = cls.prev_loglevel
        GlobalConfig.image_logging_destination = cls.prev_logpath

    def setUp(self):
        # gui test scripts
        self.script_app = os.path.join(common_test.unittest_dir, 'qt5_application.py')
        self.child_app = None

        # prefixed controls
        # NOTE: provide and use only fixed locations to avoid CV backend dependencies
        self.click_control = Location(75, 25)
        self.double_click_control = Location(185, 20)
        self.context_menu_control = Location(315, 20)
        self.context_menu_close_control = Location(355, 35)
        self.mouse_down_control = Location(435, 95)
        self.mouse_up_control = Location(435, 135)
        self.textedit_control = Location(35, 135)
        self.textedit_quit_control = Location(65, 60)
        self.textedit_any_control = Location(65, 95)
        self.drag_control = Location(435, 25)
        self.drop_control = Location(435, 65)

        self.region = Region()

    def tearDown(self):
        self.close_windows()
        if os.path.exists(GlobalConfig.image_logging_destination):
            shutil.rmtree(GlobalConfig.image_logging_destination)

    def show_application(self):
        self.child_app = subprocess.Popen(['python3', self.script_app])
        # HACK: avoid small variability in loading speed
        time.sleep(3)

    def close_windows(self):
        if self.child_app is not None:
            self.child_app.terminate()
            self.wait_end(self.child_app)
            self.child_app = None

            # HACK: make sure app is really closed
            time.sleep(0.5)

    def wait_end(self, subprocess_pipe, timeout=30):
        expires = time.time() + timeout

        while True:
            exit_code = subprocess_pipe.poll()
            if exit_code is not None:
                return exit_code

            if time.time() > expires:
                self.fail('Program did not close on time. Ignoring')
                break

            time.sleep(0.2)

    @unittest.skipIf(os.environ.get('DISABLE_AUTOPY', "0") == "1", "AutoPy disabled")
    def test_get_mouse_location(self):
        self.region.hover(Location(0, 0))
        pos = self.region.mouse_location
        # Exact match currently not possible, autopy is not pixel perfect.
        self.assertAlmostEqual(pos.x, 0, delta=1)
        self.assertAlmostEqual(pos.y, 0, delta=1)

        self.region.hover(Location(30, 20))
        pos = self.region.mouse_location
        # Exact match currently not possible, autopy is not pixel perfect.
        self.assertAlmostEqual(pos.x, 30, delta=1)
        self.assertAlmostEqual(pos.y, 20, delta=1)

    @unittest.skipIf(os.environ.get('DISABLE_OPENCV', "0") == "1" or
                     os.environ.get('DISABLE_PYQT', "0") == "1",
                     "Disabled OpenCV or PyQt")
    def test_hover(self):
        self.show_application()

        match = self.region.find('shape_green_box')
        self.region.hover(match.target)
        self.assertAlmostEqual(match.target.x, self.region.mouse_location.x, delta=1)
        self.assertAlmostEqual(match.target.y, self.region.mouse_location.y, delta=1)

        # hover over coordinates in a subregion
        match = match.find('shape_green_box')
        self.assertAlmostEqual(match.target.x, self.region.mouse_location.x, delta=1)
        self.assertAlmostEqual(match.target.y, self.region.mouse_location.y, delta=1)

        self.close_windows()

    @unittest.skipIf(os.environ.get('DISABLE_PYQT', "0") == "1", "PyQt disabled")
    def test_click(self):
        self.show_application()
        self.region.click(self.click_control)
        self.assertEqual(0, self.wait_end(self.child_app))
        self.child_app = None

    @unittest.skipIf(os.environ.get('DISABLE_PYQT', "0") == "1", "PyQt disabled")
    def test_right_click(self):
        self.show_application()
        self.region.right_click(self.context_menu_control)
        self.region.idle(3).click(self.context_menu_close_control)
        self.assertEqual(0, self.wait_end(self.child_app))
        self.child_app = None

    @unittest.skipIf(os.environ.get('DISABLE_PYQT', "0") == "1", "PyQt disabled")
    def test_double_click(self):
        self.show_application()
        self.region.double_click(self.double_click_control)
        self.assertEqual(0, self.wait_end(self.child_app))
        self.child_app = None

    @unittest.skipIf(os.environ.get('DISABLE_PYQT', "0") == "1", "PyQt disabled")
    def test_multi_click(self):
        self.show_application()
        self.region.multi_click(self.click_control, count=1)
        self.assertEqual(0, self.wait_end(self.child_app))

        self.show_application()
        self.region.multi_click(self.double_click_control, count=2)
        self.assertEqual(0, self.wait_end(self.child_app))

        self.child_app = None

    @unittest.skipIf(os.environ.get('DISABLE_OPENCV', "0") == "1" or
                     os.environ.get('DISABLE_PYQT', "0") == "1",
                     "Disabled OpenCV or PyQt")
    def test_click_expect(self):
        self.show_application()
        self.region.click_expect('shape_green_box')
        self.close_windows()

    @unittest.skipIf(os.environ.get('DISABLE_OPENCV', "0") == "1" or
                     os.environ.get('DISABLE_PYQT', "0") == "1",
                     "Disabled OpenCV or PyQt")
    def test_click_expect_different(self):
        self.show_application()
        self.region.click_expect('shape_green_box', 'shape_black_box')
        self.close_windows()

    @unittest.skipIf(os.environ.get('DISABLE_OPENCV', "0") == "1" or
                     os.environ.get('DISABLE_PYQT', "0") == "1",
                     "Disabled OpenCV or PyQt")
    def test_click_vanish(self):
        self.show_application()
        self.region.click_vanish('shape_red_box')
        self.close_windows()

    @unittest.skipIf(os.environ.get('DISABLE_OPENCV', "0") == "1" or
                     os.environ.get('DISABLE_PYQT', "0") == "1",
                     "Disabled OpenCV or PyQt")
    def test_click_vanish_different(self):
        self.show_application()
        self.region.click_vanish('shape_green_box', 'shape_red_box')
        self.close_windows()

    @unittest.skipIf(os.environ.get('DISABLE_OPENCV', "0") == "1" or
                     os.environ.get('DISABLE_PYQT', "0") == "1",
                     "Disabled OpenCV or PyQt")
    def test_click_at_index(self):
        self.show_application()
        self.region.click_at_index('shape_red_box', 0)
        self.assertEqual(0, self.wait_end(self.child_app))
        self.child_app = None

    @unittest.skipIf(os.environ.get('DISABLE_PYQT', "0") == "1", "PyQt disabled")
    def test_mouse_down(self):
        self.show_application()

        self.region.mouse_down(self.mouse_down_control)

        # toggled buttons cleanup
        self.region.dc_backend.mouse_up(self.region.LEFT_BUTTON)

        self.assertEqual(0, self.wait_end(self.child_app))
        self.child_app = None

    @unittest.skipIf(os.environ.get('DISABLE_PYQT', "0") == "1", "PyQt disabled")
    def test_mouse_up(self):
        self.show_application()

        # TODO: the GUI only works if mouse-up event is on the previous location
        # self.region.mouse_down(Location(0,0))
        # self.region.mouse_up(self.mouse_up_control)
        self.region.mouse_down(self.mouse_up_control)

        self.region.mouse_up(self.mouse_up_control)

        self.assertEqual(0, self.wait_end(self.child_app))
        self.child_app = None

    @unittest.skipIf(os.environ.get('DISABLE_PYAUTOGUI', "0") == "1", "PyAutoGUI disabled")
    @unittest.skipIf(os.environ.get('DISABLE_PYQT', "0") == "1", "PyQt disabled")
    def test_mouse_scroll(self):
        # TODO: method not available for other backends
        self.region.dc_backend = PyAutoGUIController()
        self.show_application()

        # TODO: currently we don't have any GUI components for this
        self.region.mouse_scroll(self.double_click_control)
        # cleanup since no control can close the window on scroll
        self.region.dc_backend.mouse_click(count=2)

        self.assertEqual(0, self.wait_end(self.child_app))
        self.child_app = None

    @unittest.skipIf(os.environ.get('DISABLE_PYQT', "0") == "1", "PyQt disabled")
    def test_drag_drop(self):
        self.show_application()
        self.region.drag_drop(self.textedit_control, self.textedit_quit_control)
        self.assertEqual(0, self.wait_end(self.child_app))
        self.child_app = None

    @unittest.skip("Unit test either errors out or is expected failure")
    #@unittest.expectedFailure  # hangs with PyQt5 (worked with PyQt4)
    #@unittest.skipIf(os.environ.get('DISABLE_PYQT', "0") == "1", "PyQt disabled")
    def test_drag_from(self):
        self.show_application()

        self.region.drag_from(self.textedit_control)
        self.region.hover(self.drag_control)

        # toggled buttons cleanup
        self.region.dc_backend.mouse_up(self.region.LEFT_BUTTON)

        self.assertEqual(0, self.wait_end(self.child_app))
        self.child_app = None

    @unittest.skipIf(os.environ.get('DISABLE_PYQT', "0") == "1", "PyQt disabled")
    def test_drop_at(self):
        self.show_application()

        self.region.drag_from(self.textedit_control)
        self.region.drop_at(self.drop_control)

        self.assertEqual(0, self.wait_end(self.child_app))
        self.child_app = None

    @unittest.skipIf(os.environ.get('DISABLE_PYQT', "0") == "1", "PyQt disabled")
    def test_press_keys(self):
        self.show_application()
        time.sleep(1)
        self.region.press_keys(self.region.ESC)
        self.assertEqual(0, self.wait_end(self.child_app))

        # BUG: Qt fails to register a close event in some cases
        #self.show_application()
        #time.sleep(1)
        #self.region.press_keys([self.region.ALT, self.region.F4])
        #self.assertEqual(0, self.wait_end(self.child_app))

        self.child_app = None

    @unittest.skipIf(os.environ.get('DISABLE_PYQT', "0") == "1", "PyQt disabled")
    def test_press_at(self):
        self.show_application()
        self.region.press_at([self.region.ESC], self.textedit_any_control)
        self.assertEqual(0, self.wait_end(self.child_app))
        self.child_app = None

    @unittest.skipIf(os.environ.get('DISABLE_PYQT', "0") == "1", "PyQt disabled")
    def test_type_text(self):
        self.show_application()
        self.region.click(self.textedit_quit_control)
        self.region.idle(0.2).type_text('quit')
        self.assertEqual(0, self.wait_end(self.child_app))
        self.child_app = None

    @unittest.skipIf(os.environ.get('DISABLE_PYQT', "0") == "1", "PyQt disabled")
    def test_type_at(self):
        self.show_application()
        self.region.type_at('quit', self.textedit_quit_control)
        self.assertEqual(0, self.wait_end(self.child_app))
        self.child_app = None

    @unittest.skipIf(os.environ.get('DISABLE_PYQT', "0") == "1", "PyQt disabled")
    def test_fill_at(self):
        self.show_application()
        self.region.fill_at(self.textedit_quit_control, 'quit', 0, 0)
        self.assertEqual(0, self.wait_end(self.child_app))
        self.child_app = None

    @unittest.skip("Skip due to fatal error (breaking the entire run)")
    #@unittest.expectedFailure  # autopy has a bug with arrow keys
    def test_select_at(self):
        self.show_application()
        self.region.right_click(self.context_menu_control)
        self.region.select_at(self.context_menu_close_control, 1, 0, 0, mark_clicks=0)
        self.assertEqual(0, self.wait_end(self.child_app))
        self.child_app = None
示例#15
0
 def setUp(self):
     self.child_img = None
     # initialize template matching region to support multiple matches
     GlobalConfig.hybrid_match_backend = "template"
     self.region = Region()
示例#16
0
class RegionTest(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.file_resolver = FileResolver()
        cls.file_resolver.add_path(os.path.join(common_test.unittest_dir, 'images'))

        cls.script_img = os.path.join(common_test.unittest_dir, 'qt5_image.py')

        # preserve values of static attributes
        cls.prev_loglevel = GlobalConfig.image_logging_level
        cls.prev_logpath = GlobalConfig.image_logging_destination
        GlobalConfig.image_logging_level = 0
        GlobalConfig.image_logging_destination = os.path.join(common_test.unittest_dir, 'tmp')

    @classmethod
    def tearDownClass(cls):
        GlobalConfig.image_logging_level = cls.prev_loglevel
        GlobalConfig.image_logging_destination = cls.prev_logpath

    def setUp(self):
        self.child_img = None
        # initialize template matching region to support multiple matches
        GlobalConfig.hybrid_match_backend = "template"
        self.region = Region()

    def tearDown(self):
        self.close_windows()
        if os.path.exists(GlobalConfig.image_logging_destination):
            shutil.rmtree(GlobalConfig.image_logging_destination)

    def assertAlmostIn(self, match, matches, delta=5):
        x, y = match
        for m in matches:
            mx, my = m
            if abs(x - mx) <= delta:
                if abs(y - my) <= delta:
                    return
        raise AssertionError("%s not near any of %s" % (match, matches))

    def show_image(self, filename):
        filename = self.file_resolver.search(filename)
        self.child_img = subprocess.Popen(['python3', self.script_img, filename])
        # HACK: avoid small variability in loading speed
        time.sleep(3)

    def close_windows(self):
        if self.child_img is not None:
            self.child_img.terminate()
            self.wait_end(self.child_img)
            self.child_img = None

            # make sure image is really closed
            time.sleep(0.5)

    def wait_end(self, subprocess_pipe, timeout=30):
        expires = time.time() + timeout

        while True:
            exit_code = subprocess_pipe.poll()
            if exit_code is not None:
                return exit_code

            if time.time() > expires:
                self.fail('Program did not close on time. Ignoring')
                break

            time.sleep(0.2)

    @unittest.skipIf(os.environ.get('DISABLE_AUTOPY', "0") == "1", "AutoPy disabled")
    def test_initialize(self):
        screen_width = AutoPyController().width
        screen_height = AutoPyController().height

        self.assertEqual(0, self.region.x)
        self.assertEqual(0, self.region.y)
        self.assertEqual(screen_width, self.region.width)
        self.assertEqual(screen_height, self.region.height)

        region = Region(10, 20, 300, 200)
        self.assertEqual(10, region.x)
        self.assertEqual(20, region.y)
        self.assertEqual(300, region.width)
        self.assertEqual(200, region.height)

    @unittest.skipIf(os.environ.get('DISABLE_OPENCV', "0") == "1" or
                     os.environ.get('DISABLE_PYQT', "0") == "1",
                     "Disabled OpenCV or PyQt")
    def test_find(self):
        self.show_image('all_shapes')

        match = self.region.find(Image('shape_green_box'))
        self.assertAlmostEqual(match.x, 30, delta=5)
        self.assertAlmostEqual(match.y, 190, delta=5)
        self.assertAlmostEqual(70, match.width, delta=5)
        self.assertAlmostEqual(50, match.height, delta=5)

        # Match again - this time just pass a filename
        match = self.region.find('shape_green_box')
        self.assertAlmostEqual(match.x, 30, delta=5)
        self.assertAlmostEqual(match.y, 190, delta=5)
        self.assertAlmostEqual(70, match.width, delta=5)
        self.assertAlmostEqual(50, match.height, delta=5)

        # Test last match property
        last_match = self.region.last_match
        self.assertEqual(last_match.x, match.x)
        self.assertEqual(last_match.y, match.y)
        self.assertEqual(last_match.width, match.width)
        self.assertEqual(last_match.height, match.height)

    @unittest.skipIf(os.environ.get('DISABLE_OPENCV', "0") == "1" or
                     os.environ.get('DISABLE_PYQT', "0") == "1",
                     "Disabled OpenCV or PyQt")
    def test_find_center_offset(self):
        self.show_image('all_shapes.png')

        match = self.region.find(Image('shape_blue_circle.png'))

        # Positive target offset
        match_offset = self.region.find(Image('shape_blue_circle.png').with_center_offset(200, 100))
        self.assertEqual(match.target.x + 200, match_offset.target.x)
        self.assertEqual(match.target.y + 100, match_offset.target.y)

        # Negative target offset
        match_offset = self.region.find(Image('shape_blue_circle.png').with_center_offset(-50, -30))
        self.assertEqual(match.target.x - 50, match_offset.target.x)
        self.assertEqual(match.target.y - 30, match_offset.target.y)

    @unittest.skipIf(os.environ.get('DISABLE_OPENCV', "0") == "1" or
                     os.environ.get('DISABLE_AUTOPY', "0") == "1",
                     "Disabled OpenCV or AutoPy")
    def test_find_error(self):
        try:
            self.region.find(Image('shape_blue_circle.png'), 0)
            self.fail('exception was not thrown')
        except FindError as e:
            pass

        try:
            self.region.find_all(Image('shape_blue_circle.png'), 0)
            self.fail('exception was not thrown')
        except FindError as e:
            pass

    @unittest.skipIf(os.environ.get('DISABLE_OPENCV', "0") == "1" or
                     os.environ.get('DISABLE_PYQT', "0") == "1",
                     "Disabled OpenCV or PyQt")
    def test_find_all(self):
        self.show_image('all_shapes')

        greenbox = Image('shape_green_box')
        matches = self.region.find_all(greenbox)
        self.assertEqual(len(matches), 1)
        self.assertAlmostEqual(matches[0].x, 30, delta=5)
        self.assertAlmostEqual(matches[0].y, 190, delta=5)
        self.assertAlmostEqual(70, matches[0].width, delta=5)
        self.assertAlmostEqual(50, matches[0].height, delta=5)

        redbox = Image('shape_red_box')
        matches = self.region.find_all(redbox)
        expected_matches = [(25, 25), (320, 25), (315, 115)]
        self.assertEqual(len(matches), len(expected_matches))
        for match in matches:
            self.region.hover(match)
            time.sleep(0.5)
            self.assertAlmostIn((match.x, match.y), expected_matches)
            self.assertAlmostEqual(70, match.width, delta=5)
            self.assertAlmostEqual(60, match.height, delta=5)

        pinkbox = Image('shape_pink_box')
        # pink is similar to red, so the best fuzzy matches also
        # include the three red boxes when considering color
        self.region.cv_backend.matcher.params["find"]["similarity"].value = 0.5
        self.region.cv_backend.matcher.params["template"]["nocolor"].value = False
        matches = self.region.find_all(pinkbox)
        # approximately the above coordinates since maching different needle
        expected_matches = [(25, 35), (320, 40), (320, 125), (30, 255)]
        self.assertEqual(len(matches), len(expected_matches))
        for match in matches:
            self.region.hover(match)
            time.sleep(0.5)
            self.assertAlmostIn((match.x, match.y), expected_matches)
            self.assertAlmostEqual(70, match.width, delta=5)
            self.assertAlmostEqual(50, match.height, delta=5)

        # ignore colors here so the best matches for the pink box
        # should be based on shape (the green and yellow box)
        self.region.cv_backend.matcher.params["find"]["similarity"].value = 0.8
        self.region.cv_backend.matcher.params["template"]["nocolor"].value = True
        matches = self.region.find_all(pinkbox)
        expected_matches = [(30, 120), (30, 195), (30, 255)]
        self.assertEqual(len(matches), len(expected_matches))
        for match in matches:
            self.region.hover(match)
            time.sleep(0.5)
            self.assertAlmostIn((match.x, match.y), expected_matches)
            self.assertAlmostEqual(70, match.width, delta=5)
            self.assertAlmostEqual(50, match.height, delta=5)

    @unittest.skipIf(os.environ.get('DISABLE_OPENCV', "0") == "1" or
                     os.environ.get('DISABLE_PYQT', "0") == "1",
                     "Disabled OpenCV or PyQt")
    def test_find_zero_matches(self):
        self.show_image('all_shapes')

        matches = self.region.find_all(Image('shape_blue_circle'))
        self.assertEqual(len(matches), 1)
        self.close_windows()

        matches = self.region.find_all(Image('shape_blue_circle'), allow_zero=True)
        self.assertEqual(len(matches), 0)
        self.close_windows()

    @unittest.skipIf(os.environ.get('DISABLE_OPENCV', "0") == "1" or
                     os.environ.get('DISABLE_OCR', "0") == "1",
                     "Disabled OpenCV or OCR")
    def test_find_guess_target(self):
        self.show_image('all_shapes')
        imgroot = os.path.join(common_test.unittest_dir, 'images')

        # find image from string with and without extension
        self.assertFalse(os.path.exists(os.path.join(imgroot, 'shape_blue_circle.match')))
        self.assertTrue(os.path.exists(os.path.join(imgroot, 'shape_blue_circle.png')))
        self.region.find('shape_blue_circle')
        self.region.find_all('shape_blue_circle')
        self.region.find('shape_blue_circle.png')
        self.region.find_all('shape_blue_circle.png')

        # guess from match file configuration (target has match config)
        # precedence is given to match file configuration (then data file)
        self.assertTrue(os.path.exists(os.path.join(imgroot, 'mouse down.match')))
        self.assertTrue(os.path.exists(os.path.join(imgroot, 'mouse down.txt')))
        try:
            self.region.find('mouse down')
            self.fail('exception was not thrown')
        except FindError as e:
            pass
        try:
            self.region.find_all('mouse down')
            self.fail('exception was not thrown')
        except FindError as e:
            pass

        # guess from data file extension (target has no match config)
        self.assertFalse(os.path.exists(os.path.join(imgroot, 'circle.match')))
        self.assertTrue(os.path.exists(os.path.join(imgroot, 'circle.steps')))
        self.region.find('circle')
        self.region.find_all('circle')

        # end with default type if also unknown data type
        self.assertFalse(os.path.exists(os.path.join(imgroot, 'shape_blue_circle_unknown.match')))
        self.assertTrue(os.path.exists(os.path.join(imgroot, 'shape_blue_circle_unknown.xtx')))
        self.region.default_target_type = Image
        # NOTE: autopy cannot handle a masked image
        self.region.find('shape_blue_circle_unknown.xtx')
        self.region.find_all('shape_blue_circle_unknown.xtx')

        # do not fail with default text type if also missing data file
        self.assertFalse(os.path.exists(os.path.join(imgroot, 'mouse somewhere.match')))
        self.assertFalse(os.path.exists(os.path.join(imgroot, 'mouse somewhere.txt')))
        self.region.default_target_type = Text
        self.region.cv_backend = TextFinder()
        try:
            self.region.find('mouse somewhere')
            self.fail('exception was not thrown')
        except FindError as e:
            pass
        try:
            self.region.find_all('mouse somewhere')
            self.fail('exception was not thrown')
        except FindError as e:
            pass

    @unittest.skipIf(os.environ.get('DISABLE_OPENCV', "0") == "1" or
                     os.environ.get('DISABLE_PYQT', "0") == "1",
                     "Disabled OpenCV or PyQt")
    def test_sample(self):
        self.show_image('all_shapes')

        # autopy matching does not support similarity
        shapes = Region(cv=AutoPyFinder())
        similarity = shapes.sample(Image('shape_blue_circle'))
        self.assertEqual(similarity, 0.0)

        # initialize template matching region to support similarity
        shapes = Region(cv=TemplateFinder())
        similarity = shapes.sample(Image('shape_blue_circle'))
        self.assertAlmostEqual(similarity, 0.999999, delta=0.001)

        self.close_windows()

    @unittest.skipIf(os.environ.get('DISABLE_OPENCV', "0") == "1" or
                     os.environ.get('DISABLE_PYQT', "0") == "1",
                     "Disabled OpenCV or PyQt")
    def test_exists(self):
        self.show_image('all_shapes')

        match = self.region.find(Image('shape_blue_circle'))
        self.assertTrue(isinstance(match, Match))

        self.close_windows()

        match = self.region.exists(Image('shape_blue_circle'))
        self.assertEqual(None, match)

    @unittest.skipIf(os.environ.get('DISABLE_OPENCV', "0") == "1" or
                     os.environ.get('DISABLE_PYQT', "0") == "1",
                     "Disabled OpenCV or PyQt")
    def test_wait(self):
        self.show_image('all_shapes')

        match = self.region.wait(Image('shape_blue_circle'), timeout=5)
        self.assertTrue(isinstance(match, Match))

        self.close_windows()

    @unittest.skipIf(os.environ.get('DISABLE_OPENCV', "0") == "1" or
                     os.environ.get('DISABLE_PYQT', "0") == "1",
                     "Disabled OpenCV or PyQt")
    def test_wait_vanish(self):
        self.show_image('all_shapes')

        self.assertRaises(NotFindError, self.region.wait_vanish, 'all_shapes', timeout=10)

        self.close_windows()

        # assert no NotFindError is raised now
        self.assertTrue(self.region.wait_vanish('all_shapes', timeout=10))