Exemple #1
0
 def setUp(self):
     self.lh = logging.NullHandler()
     logging.getLogger(driver.LOGGER_NAME).addHandler(self.lh)
     self.game = Minesweeper()
Exemple #2
0
 def setUp(self):
     self.lh = logging.NullHandler()
     logging.getLogger(driver.LOGGER_NAME).addHandler(self.lh)
     self.game = Minesweeper()
Exemple #3
0
class MinesweeperTest(unittest.TestCase):
    def setUp(self):
        self.lh = logging.NullHandler()
        logging.getLogger(driver.LOGGER_NAME).addHandler(self.lh)
        self.game = Minesweeper()

    def test_new_game(self):
        self.assertIsNone(self.game.size())
        self.assertIsNone(self.game.bombs())
        self.assertIsNone(self.game.flags())
        self.assertFalse(self.game.is_win())
        self.assertFalse(self.game.is_lose())
        self.assertTrue(self.game.saved())
        self.assertEqual(0, self.game.get_time())

        self.game.new_game((5, 6), 8)
        self.assertEqual((5, 6), self.game.size())
        self.assertEqual(8, self.game.bombs())
        self.assertEqual(0, self.game.flags())
        self.assertFalse(self.game.is_win())
        self.assertFalse(self.game.is_lose())
        self.assertTrue(self.game.saved())
        self.assertEqual(0, self.game.get_time())

    def test_handlers(self):
        counter = {event_type: 0 for event_type in EventTypes}

        def handle(event):
            counter[event] += 1

        handlers = {}
        for evt_type in EventTypes:
            handlers[evt_type] = (lambda e=evt_type: lambda *args: handle(e))()
            self.game.add_event_handler(evt_type, handlers[evt_type])

        def remove_handler(event):
            self.game.remove_event_handler(event, handlers[event])

        with patch_field_generator(Field((2, 1), {(0, 0)})):
            self.game.new_game((2, 1), 1)

        with self.subTest('changed handler'):
            self.assertEqual(0, counter[EventTypes.CELL_CHANGED])
            self.game.invert_flag((0, 0))
            self.assertEqual(1, counter[EventTypes.CELL_CHANGED])
            remove_handler(EventTypes.CELL_CHANGED)
            self.game.invert_flag((0, 0))
            self.assertEqual(1, counter[EventTypes.CELL_CHANGED])

        with self.subTest('win handler'):
            self.game.again()
            self.assertEqual(0, counter[EventTypes.PLAYER_WIN])
            self.game.open_cell((1, 0))
            self.game.invert_flag((0, 0))
            self.assertEqual(1, counter[EventTypes.PLAYER_WIN])
            self.game.again()
            remove_handler(EventTypes.PLAYER_WIN)
            self.game.open_cell((1, 0))
            self.game.invert_flag((0, 0))
            self.assertEqual(1, counter[EventTypes.PLAYER_WIN])

        with self.subTest('lose handler'):
            self.game.again()
            self.assertEqual(0, counter[EventTypes.PLAYER_LOSE])
            self.game.open_cell((0, 0))
            self.assertEqual(1, counter[EventTypes.PLAYER_LOSE])
            self.game.again()
            remove_handler(EventTypes.PLAYER_LOSE)
            self.game.open_cell((0, 0))
            self.assertEqual(1, counter[EventTypes.PLAYER_LOSE])

        with self.subTest('new_game handler'):
            counter[EventTypes.NEW_GAME] = 0
            self.game.again()
            self.assertEqual(1, counter[EventTypes.NEW_GAME])
            remove_handler(EventTypes.NEW_GAME)
            self.game.again()
            self.assertEqual(1, counter[EventTypes.NEW_GAME])

        with self.subTest('end_change handler'):
            counter[EventTypes.END_CHANGE] = 0
            self.game.again()
            self.assertEqual(1, counter[EventTypes.END_CHANGE])
            self.game.invert_flag((0, 0))
            self.assertEqual(2, counter[EventTypes.END_CHANGE])
            self.game.invert_flag((0, 0))
            self.assertEqual(3, counter[EventTypes.END_CHANGE])
            self.game.open_cell((1, 0))
            self.assertEqual(4, counter[EventTypes.END_CHANGE])
            self.game.undo()
            self.assertEqual(5, counter[EventTypes.END_CHANGE])
            self.game.redo()
            self.assertEqual(6, counter[EventTypes.END_CHANGE])
            remove_handler(EventTypes.END_CHANGE)
            self.game.undo()
            self.assertEqual(6, counter[EventTypes.END_CHANGE])

    def test_handler_decorator(self):
        callargs = []

        @self.game.event_handler(EventTypes.NEW_GAME)
        def handler(*args):
            callargs.append(args)

        self.game.new_game((5, 5), 10)
        self.assertListEqual([((5, 5), 10)], callargs)

    def test_open_cell(self):
        callargs = []
        self.game.add_event_handler(EventTypes.CELL_CHANGED,
                                    lambda *args: callargs.append(args))

        self.game.open_cell((0, 0))
        self.assertListEqual([], callargs)

        with patch_field_generator(Field((4, 3), {(0, 0), (2, 0)})):
            self.game.new_game((4, 3), 2)

        self.game.open_cell((3, 2))
        self.assertEqual(8, len(callargs))
        self.assertSetEqual(
            {e[0]
             for e in callargs if e[1] == CellState.OPENED},
            {(x, y)
             for x in (0, 1, 2, 3) for y in (1, 2)})

        self.game.again()
        callargs = []
        self.game.open_cell(('3', '2'))
        self.assertEqual(8, len(callargs))

        self.assertFalse(self.game.is_win())
        self.game.open_cell((3, 2))
        self.assertEqual(8, len(callargs))

    def test_autocomplete(self):
        callargs = []
        self.game.add_event_handler(EventTypes.CELL_CHANGED,
                                    lambda *args: callargs.append(args))

        with patch_field_generator(Field((3, 3), {(0, 0), (2, 0)})):
            self.game.new_game((3, 3), 2)

        self.game.open_cell((1, 2))
        self.game.open_cell((1, 0))
        self.assertFalse(self.game.is_win())

        self.game.again()
        self.game.open_cell((1, 2))
        self.game.open_cell((1, 0), True)
        self.assertTrue(self.game.is_win())
        self.assertSetEqual({(0, 0), (2, 0)},
                            {e[0]
                             for e in callargs if e[1] == CellState.FLAG})

        self.game.again()
        self.game.invert_flag((1, 0))
        self.game.open_cell((1, 2), True)
        self.assertFalse(self.game.is_win())

    def test_neighbors(self):
        self.assertIsNone(self.game.get_neighbors((0, 0)))

        with patch_field_generator(Field((3, 3), {(0, 0), (2, 0)})):
            self.game.new_game((3, 3), 2)

        self.game.open_cell((2, 2))
        self.assertListEqual(
            [1, 2, 1], [self.game.get_neighbors((x, 1)) for x in (0, 1, 2)])
        self.assertListEqual(
            [1, 2, 1], [self.game.get_neighbors((x, '1')) for x in (0, 1, 2)])
        self.assertListEqual(
            [0, 0, 0], [self.game.get_neighbors((x, 2)) for x in (0, 1, 2)])

    def test_state(self):
        self.assertIsNone(self.game.get_state())

        with patch_field_generator(Field((2, 2), {(0, 0)})):
            self.game.new_game((2, 2), 1)

        callargs = []
        self.game.add_event_handler(EventTypes.CELL_CHANGED,
                                    lambda *args: callargs.append(args))

        self.game.open_cell((1, 1))
        state = self.game.get_state()
        self.assertEqual(CellState.OPENED, state.get_state((1, 1)))

        self.assertEqual(1, len(callargs))
        state.open_cell((1, 0))
        self.assertEqual(1, len(callargs))
        state.set_flag((0, 0))
        self.assertEqual(0, self.game.flags())

    def test_time(self):
        with self.subTest('flags'):
            self.game.new_game((3, 3), 2)

            self.assertEqual(0, self.game.get_time())
            time.sleep(0.1)
            self.assertEqual(0, self.game.get_time())

            self.game.invert_flag((0, 0))
            time.sleep(0.1)
            self.assertGreaterEqual(self.game.get_time(), 10)

        with self.subTest('open'):
            with patch_field_generator(Field((3, 3), {(0, 0), (2, 0)})):
                self.game.new_game((3, 3), 2)

            self.assertEqual(0, self.game.get_time())
            self.game.open_cell((2, 2))
            time.sleep(0.2)
            self.assertGreaterEqual(self.game.get_time(), 20)

        with self.subTest('load'):
            self.game.again()
            self.game.open_cell((2, 2))
            time.sleep(0.15)

            try:
                with tempfile.NamedTemporaryFile(delete=False) as f:
                    name = f.name

                self.game.save_game(name)

                self.game.new_game((10, 10), 10)
                self.game.load_game(name)

                self.assertGreaterEqual(self.game.get_time(), 15)
                time.sleep(0.1)
                self.assertGreaterEqual(self.game.get_time(), 25)
            finally:
                if os.path.exists(name):
                    os.remove(name)

    def test_flags(self):
        callargs = []
        self.game.add_event_handler(EventTypes.CELL_CHANGED,
                                    lambda *args: callargs.append(args))

        self.game.invert_flag((0, 0))
        self.assertListEqual([], callargs)

        with patch_field_generator(Field((4, 3), {(0, 0), (2, 0)})):
            self.game.new_game((4, 3), 2)

        self.game.invert_flag((0, 2))
        self.assertListEqual([((0, 2), CellState.FLAG)], callargs)
        self.game.open_cell((3, 2))
        self.assertEqual(8, len(callargs))

        self.game.invert_flag(('0', '2'))
        self.assertEqual(((0, 2), CellState.UNKNOWN), callargs[-1])

        self.game.invert_flag((0, 0))
        self.assertFalse(self.game.is_lose())
        self.game.open_cell((0, 0))
        self.assertFalse(self.game.is_lose())

    def test_new_game_handler(self):
        callargs = []
        self.game.add_event_handler(EventTypes.NEW_GAME,
                                    lambda *args: callargs.append(args))

        self.game.new_game((4, 3), 2)
        self.assertEqual(((4, 3), 2), callargs[0])

        self.game.new_game((8, 8), 10)
        self.assertEqual(((8, 8), 10), callargs[1])

        self.game.again()
        self.assertEqual(((8, 8), 10), callargs[2])

        self.game.new_game((5, 4), 7)
        self.assertEqual(((5, 4), 7), callargs[3])

        self.game.again()
        self.assertEqual(((5, 4), 7), callargs[4])

        self.game.again()
        self.assertEqual(((5, 4), 7), callargs[5])

    def test_again(self):
        with patch_field_generator(Field((2, 2), {(0, 0)})):
            self.game.new_game((2, 2), 1)

        self.game.invert_flag((0, 0))
        self.assertEqual(1, self.game.flags())
        self.game.again()
        self.assertEqual(0, self.game.flags())

        for coords in ((0, 1), (1, 0), (1, 1)):
            self.game.open_cell(coords)
        self.assertFalse(self.game.is_win())
        self.game.invert_flag((0, 0))
        self.assertTrue(self.game.is_win())
        self.game.again()
        self.assertFalse(self.game.is_win())

        self.assertFalse(self.game.is_lose())
        self.game.open_cell((0, 0))
        self.assertTrue(self.game.is_lose())
        self.game.again()
        self.assertFalse(self.game.is_lose())

    def test_win(self):
        field = Field((3, 3), {(0, 0), (2, 0)})
        with patch_field_generator(field):
            self.game.new_game((3, 3), 2)

        callargs = []
        self.game.add_event_handler(EventTypes.PLAYER_WIN,
                                    lambda *args: callargs.append(args))

        self.game.open_cell((1, 2))
        self.assertFalse(self.game.is_win())
        self.assertListEqual([], callargs)

        self.game.open_cell((1, 0))
        self.assertFalse(self.game.is_win())
        self.assertListEqual([], callargs)

        self.game.invert_flag((0, 0))
        self.game.invert_flag((2, 0))
        self.assertTrue(self.game.is_win())
        self.assertTrue(self.game.saved())
        self.assertListEqual([(field, )], callargs)

    def test_lose(self):
        field = Field((3, 3), {(0, 0), (2, 0)})
        with patch_field_generator(field):
            self.game.new_game((3, 3), 2)

        callargs = []
        self.game.add_event_handler(EventTypes.PLAYER_LOSE,
                                    lambda *args: callargs.append(args))

        self.game.open_cell((1, 2))
        self.assertFalse(self.game.is_lose())
        self.assertListEqual([], callargs)

        self.game.open_cell((0, 0))
        self.assertTrue(self.game.is_lose())
        self.assertTrue(self.game.saved())
        self.assertListEqual([(field, (0, 0))], callargs)

    def test_undo_redo(self):
        with patch_field_generator(Field((4, 3), {(0, 0), (3, 1)})):
            self.game.new_game((4, 3), 2)

        callargs = []
        self.game.add_event_handler(EventTypes.CELL_CHANGED,
                                    lambda *args: callargs.append(args))

        self.assertFalse(self.game.can_undo())
        self.assertFalse(self.game.can_redo())

        self.game.undo()
        self.game.redo()
        self.assertListEqual([], callargs)

        with self.subTest('flags'):
            self.game.invert_flag((0, 0))
            self.assertTrue(self.game.can_undo())
            self.assertEqual(1, self.game.flags())

            self.game.undo()
            self.assertFalse(self.game.can_undo())
            self.assertTrue(self.game.can_redo())
            self.assertEqual(0, self.game.flags())

            self.game.invert_flag((0, 0))
            self.assertFalse(self.game.can_redo())
            self.assertEqual(1, self.game.flags())

            self.game.undo()
            self.assertEqual(0, self.game.flags())
            self.game.redo()
            self.assertEqual(1, self.game.flags())

            self.assertEqual(5, len(callargs))

        with self.subTest('again'):
            self.assertTrue(self.game.can_undo() or self.game.can_redo())
            self.game.again()
            self.assertFalse(self.game.can_undo() or self.game.can_redo())

        with self.subTest('wrong_flags'):
            self.game.open_cell((2, 2))
            self.game.invert_flag((2, 2))
            self.game.undo()
            self.assertFalse(self.game.can_undo())

        with self.subTest('open'):
            self.game.again()
            callargs = []
            self.game.open_cell((2, 0))
            self.game.open_cell((0, 2))
            self.game.open_cell((1, 0))
            self.assertTrue(self.game.can_undo())
            self.game.undo()
            self.assertEqual(CellState.UNKNOWN, callargs[-1][1])
            self.assertEqual(CellState.OPENED, callargs[-2][1])

            self.assertTrue(self.game.can_undo())
            self.game.undo()
            self.assertListEqual([CellState.UNKNOWN] * 6,
                                 [x[1] for x in callargs[-6:]])

            self.game.redo()
            self.assertListEqual([CellState.OPENED] * 6,
                                 [x[1] for x in callargs[-6:]])

            self.assertTrue(self.game.can_redo())
            self.game.open_cell((1, 2))

        with patch_field_generator(Field((2, 1), {(0, 0)})):
            self.game.new_game((2, 1), 1)

        with self.subTest('lose'):
            self.game.open_cell((1, 0))
            self.assertFalse(self.game.is_lose())
            self.game.open_cell((0, 0))
            self.assertTrue(self.game.is_lose())
            self.assertFalse(self.game.can_undo() or self.game.can_redo())

        with self.subTest('win'):
            self.game.again()
            self.game.open_cell((1, 0))
            self.assertFalse(self.game.is_win())
            self.game.invert_flag((0, 0))
            self.assertTrue(self.game.is_win())
            self.assertFalse(self.game.can_undo() or self.game.can_redo())

    @unittest.skipIf(sys.platform.startswith('win'), 'Windows not supported')
    def test_load_bad(self):
        with tempfile.NamedTemporaryFile(delete=False) as f:
            name = f.name
            f.write(b'BAD DATA')

        old_mode = stat.S_IMODE(os.stat(name).st_mode)

        try:
            with self.assertRaises(driver.LoadError):
                self.game.load_game(name)

            self.game.new_game((4, 4), 3)
            self.game.save_game(name)
            self.assertTrue(os.path.exists(name))
            os.chmod(name, 0)
            with self.assertRaises(driver.LoadError):
                self.game.load_game(name)

            self.game.new_game((4, 4), 3)
            self.game.invert_flag((0, 0))

            os.remove(name)
            self.assertFalse(os.path.exists(name))
            with self.assertRaises(driver.LoadError):
                self.game.load_game(name)

            self.assertTrue(1, self.game.flags())
        finally:
            if os.path.exists(name):
                os.chmod(name, old_mode)
                os.remove(name)

    def test_save_bad(self):
        with tempfile.NamedTemporaryFile(delete=False) as f:
            name = f.name

        old_mode = stat.S_IMODE(os.stat(name).st_mode)

        try:
            with self.assertRaises(driver.SaveError):
                self.game.save_game(name)

            self.game.new_game((4, 4), 3)
            os.chmod(name, 0)
            with self.assertRaises(driver.SaveError):
                self.game.save_game(name)
        finally:
            if os.path.exists(name):
                os.chmod(name, old_mode)
                os.remove(name)

    def test_save_load(self):
        with tempfile.NamedTemporaryFile(delete=False) as f:
            name = f.name

        try:
            with patch_field_generator(Field((4, 3), {(0, 0), (3, 1)})):
                self.game.new_game((4, 3), 2)

            self.game.open_cell((0, 2))
            self.game.invert_flag((0, 0))
            self.game.invert_flag((1, 0))
            time.sleep(0.3)
            self.game.undo()
            self.assertFalse(self.game.saved())
            self.game.save_game(name)
            self.assertTrue(self.game.saved())
            self.game.new_game((5, 5), 10)

            callargs = []
            for evt_type in (EventTypes.NEW_GAME, EventTypes.CELL_CHANGED):
                self.game.add_event_handler(
                    evt_type, lambda *args: callargs.append(args))

            self.game.load_game(name)
            self.assertEqual(((4, 3), 2), callargs[0])
            self.assertSetEqual(
                {(0, 0)}, {x[0]
                           for x in callargs if x[1] == CellState.FLAG})
            self.assertSetEqual(
                {(x, y)
                 for x in (0, 1, 2) for y in (1, 2)},
                {x[0]
                 for x in callargs if x[1] == CellState.OPENED})
            self.assertSetEqual(
                {(1, 0), (2, 0), (3, 0), (3, 1), (3, 2)},
                {x[0]
                 for x in callargs if x[1] == CellState.UNKNOWN})
            self.assertGreaterEqual(self.game.get_time(), 30)
            self.assertTrue(self.game.can_undo())
        finally:
            if os.path.exists(name):
                os.remove(name)

    def tearDown(self):
        logging.getLogger(driver.LOGGER_NAME).removeHandler(self.lh)
Exemple #4
0
class MinesweeperTest(unittest.TestCase):
    def setUp(self):
        self.lh = logging.NullHandler()
        logging.getLogger(driver.LOGGER_NAME).addHandler(self.lh)
        self.game = Minesweeper()

    def test_new_game(self):
        self.assertIsNone(self.game.size())
        self.assertIsNone(self.game.bombs())
        self.assertIsNone(self.game.flags())
        self.assertFalse(self.game.is_win())
        self.assertFalse(self.game.is_lose())
        self.assertTrue(self.game.saved())
        self.assertEqual(0, self.game.get_time())

        self.game.new_game((5, 6), 8)
        self.assertEqual((5, 6), self.game.size())
        self.assertEqual(8, self.game.bombs())
        self.assertEqual(0, self.game.flags())
        self.assertFalse(self.game.is_win())
        self.assertFalse(self.game.is_lose())
        self.assertTrue(self.game.saved())
        self.assertEqual(0, self.game.get_time())

    def test_handlers(self):
        counter = {event_type: 0 for event_type in EventTypes}

        def handle(event):
            counter[event] += 1

        handlers = {}
        for evt_type in EventTypes:
            handlers[evt_type] = (lambda e=evt_type: lambda *args: handle(e))()
            self.game.add_event_handler(evt_type, handlers[evt_type])

        def remove_handler(event):
            self.game.remove_event_handler(event, handlers[event])

        with patch_field_generator(Field((2, 1), {(0, 0)})):
            self.game.new_game((2, 1), 1)

        with self.subTest('changed handler'):
            self.assertEqual(0, counter[EventTypes.CELL_CHANGED])
            self.game.invert_flag((0, 0))
            self.assertEqual(1, counter[EventTypes.CELL_CHANGED])
            remove_handler(EventTypes.CELL_CHANGED)
            self.game.invert_flag((0, 0))
            self.assertEqual(1, counter[EventTypes.CELL_CHANGED])

        with self.subTest('win handler'):
            self.game.again()
            self.assertEqual(0, counter[EventTypes.PLAYER_WIN])
            self.game.open_cell((1, 0))
            self.game.invert_flag((0, 0))
            self.assertEqual(1, counter[EventTypes.PLAYER_WIN])
            self.game.again()
            remove_handler(EventTypes.PLAYER_WIN)
            self.game.open_cell((1, 0))
            self.game.invert_flag((0, 0))
            self.assertEqual(1, counter[EventTypes.PLAYER_WIN])

        with self.subTest('lose handler'):
            self.game.again()
            self.assertEqual(0, counter[EventTypes.PLAYER_LOSE])
            self.game.open_cell((0, 0))
            self.assertEqual(1, counter[EventTypes.PLAYER_LOSE])
            self.game.again()
            remove_handler(EventTypes.PLAYER_LOSE)
            self.game.open_cell((0, 0))
            self.assertEqual(1, counter[EventTypes.PLAYER_LOSE])

        with self.subTest('new_game handler'):
            counter[EventTypes.NEW_GAME] = 0
            self.game.again()
            self.assertEqual(1, counter[EventTypes.NEW_GAME])
            remove_handler(EventTypes.NEW_GAME)
            self.game.again()
            self.assertEqual(1, counter[EventTypes.NEW_GAME])

        with self.subTest('end_change handler'):
            counter[EventTypes.END_CHANGE] = 0
            self.game.again()
            self.assertEqual(1, counter[EventTypes.END_CHANGE])
            self.game.invert_flag((0, 0))
            self.assertEqual(2, counter[EventTypes.END_CHANGE])
            self.game.invert_flag((0, 0))
            self.assertEqual(3, counter[EventTypes.END_CHANGE])
            self.game.open_cell((1, 0))
            self.assertEqual(4, counter[EventTypes.END_CHANGE])
            self.game.undo()
            self.assertEqual(5, counter[EventTypes.END_CHANGE])
            self.game.redo()
            self.assertEqual(6, counter[EventTypes.END_CHANGE])
            remove_handler(EventTypes.END_CHANGE)
            self.game.undo()
            self.assertEqual(6, counter[EventTypes.END_CHANGE])

    def test_handler_decorator(self):
        callargs = []

        @self.game.event_handler(EventTypes.NEW_GAME)
        def handler(*args):
            callargs.append(args)

        self.game.new_game((5, 5), 10)
        self.assertListEqual([((5, 5), 10)], callargs)

    def test_open_cell(self):
        callargs = []
        self.game.add_event_handler(EventTypes.CELL_CHANGED,
                                    lambda *args: callargs.append(args))

        self.game.open_cell((0, 0))
        self.assertListEqual([], callargs)

        with patch_field_generator(Field((4, 3), {(0, 0), (2, 0)})):
            self.game.new_game((4, 3), 2)

        self.game.open_cell((3, 2))
        self.assertEqual(8, len(callargs))
        self.assertSetEqual(
            {e[0] for e in callargs if e[1] == CellState.OPENED},
            {(x, y) for x in (0, 1, 2, 3) for y in (1, 2)})

        self.game.again()
        callargs = []
        self.game.open_cell(('3', '2'))
        self.assertEqual(8, len(callargs))

        self.assertFalse(self.game.is_win())
        self.game.open_cell((3, 2))
        self.assertEqual(8, len(callargs))

    def test_autocomplete(self):
        callargs = []
        self.game.add_event_handler(EventTypes.CELL_CHANGED,
                                    lambda *args: callargs.append(args))

        with patch_field_generator(Field((3, 3), {(0, 0), (2, 0)})):
            self.game.new_game((3, 3), 2)

        self.game.open_cell((1, 2))
        self.game.open_cell((1, 0))
        self.assertFalse(self.game.is_win())

        self.game.again()
        self.game.open_cell((1, 2))
        self.game.open_cell((1, 0), True)
        self.assertTrue(self.game.is_win())
        self.assertSetEqual({(0, 0), (2, 0)},
                            {e[0] for e in callargs if e[1] == CellState.FLAG})

        self.game.again()
        self.game.invert_flag((1, 0))
        self.game.open_cell((1, 2), True)
        self.assertFalse(self.game.is_win())

    def test_neighbors(self):
        self.assertIsNone(self.game.get_neighbors((0, 0)))

        with patch_field_generator(Field((3, 3), {(0, 0), (2, 0)})):
            self.game.new_game((3, 3), 2)

        self.game.open_cell((2, 2))
        self.assertListEqual(
            [1, 2, 1], [self.game.get_neighbors((x, 1)) for x in (0, 1, 2)])
        self.assertListEqual(
            [1, 2, 1], [self.game.get_neighbors((x, '1')) for x in (0, 1, 2)])
        self.assertListEqual(
            [0, 0, 0], [self.game.get_neighbors((x, 2)) for x in (0, 1, 2)])

    def test_state(self):
        self.assertIsNone(self.game.get_state())

        with patch_field_generator(Field((2, 2), {(0, 0)})):
            self.game.new_game((2, 2), 1)

        callargs = []
        self.game.add_event_handler(EventTypes.CELL_CHANGED,
                                    lambda *args: callargs.append(args))

        self.game.open_cell((1, 1))
        state = self.game.get_state()
        self.assertEqual(CellState.OPENED, state.get_state((1, 1)))

        self.assertEqual(1, len(callargs))
        state.open_cell((1, 0))
        self.assertEqual(1, len(callargs))
        state.set_flag((0, 0))
        self.assertEqual(0, self.game.flags())

    def test_time(self):
        with self.subTest('flags'):
            self.game.new_game((3, 3), 2)

            self.assertEqual(0, self.game.get_time())
            time.sleep(0.1)
            self.assertEqual(0, self.game.get_time())

            self.game.invert_flag((0, 0))
            time.sleep(0.1)
            self.assertGreaterEqual(self.game.get_time(), 10)

        with self.subTest('open'):
            with patch_field_generator(Field((3, 3), {(0, 0), (2, 0)})):
                self.game.new_game((3, 3), 2)

            self.assertEqual(0, self.game.get_time())
            self.game.open_cell((2, 2))
            time.sleep(0.2)
            self.assertGreaterEqual(self.game.get_time(), 20)

        with self.subTest('load'):
            self.game.again()
            self.game.open_cell((2, 2))
            time.sleep(0.15)

            try:
                with tempfile.NamedTemporaryFile(delete=False) as f:
                    name = f.name

                self.game.save_game(name)

                self.game.new_game((10, 10), 10)
                self.game.load_game(name)

                self.assertGreaterEqual(self.game.get_time(), 15)
                time.sleep(0.1)
                self.assertGreaterEqual(self.game.get_time(), 25)
            finally:
                if os.path.exists(name):
                    os.remove(name)

    def test_flags(self):
        callargs = []
        self.game.add_event_handler(EventTypes.CELL_CHANGED,
                                    lambda *args: callargs.append(args))

        self.game.invert_flag((0, 0))
        self.assertListEqual([], callargs)

        with patch_field_generator(Field((4, 3), {(0, 0), (2, 0)})):
            self.game.new_game((4, 3), 2)

        self.game.invert_flag((0, 2))
        self.assertListEqual([((0, 2), CellState.FLAG)], callargs)
        self.game.open_cell((3, 2))
        self.assertEqual(8, len(callargs))

        self.game.invert_flag(('0', '2'))
        self.assertEqual(((0, 2), CellState.UNKNOWN), callargs[-1])

        self.game.invert_flag((0, 0))
        self.assertFalse(self.game.is_lose())
        self.game.open_cell((0, 0))
        self.assertFalse(self.game.is_lose())

    def test_new_game_handler(self):
        callargs = []
        self.game.add_event_handler(EventTypes.NEW_GAME,
                                    lambda *args: callargs.append(args))

        self.game.new_game((4, 3), 2)
        self.assertEqual(((4, 3), 2), callargs[0])

        self.game.new_game((8, 8), 10)
        self.assertEqual(((8, 8), 10), callargs[1])

        self.game.again()
        self.assertEqual(((8, 8), 10), callargs[2])

        self.game.new_game((5, 4), 7)
        self.assertEqual(((5, 4), 7), callargs[3])

        self.game.again()
        self.assertEqual(((5, 4), 7), callargs[4])

        self.game.again()
        self.assertEqual(((5, 4), 7), callargs[5])

    def test_again(self):
        with patch_field_generator(Field((2, 2), {(0, 0)})):
            self.game.new_game((2, 2), 1)

        self.game.invert_flag((0, 0))
        self.assertEqual(1, self.game.flags())
        self.game.again()
        self.assertEqual(0, self.game.flags())

        for coords in ((0, 1), (1, 0), (1, 1)):
            self.game.open_cell(coords)
        self.assertFalse(self.game.is_win())
        self.game.invert_flag((0, 0))
        self.assertTrue(self.game.is_win())
        self.game.again()
        self.assertFalse(self.game.is_win())

        self.assertFalse(self.game.is_lose())
        self.game.open_cell((0, 0))
        self.assertTrue(self.game.is_lose())
        self.game.again()
        self.assertFalse(self.game.is_lose())

    def test_win(self):
        field = Field((3, 3), {(0, 0), (2, 0)})
        with patch_field_generator(field):
            self.game.new_game((3, 3), 2)

        callargs = []
        self.game.add_event_handler(EventTypes.PLAYER_WIN,
                                    lambda *args: callargs.append(args))

        self.game.open_cell((1, 2))
        self.assertFalse(self.game.is_win())
        self.assertListEqual([], callargs)

        self.game.open_cell((1, 0))
        self.assertFalse(self.game.is_win())
        self.assertListEqual([], callargs)

        self.game.invert_flag((0, 0))
        self.game.invert_flag((2, 0))
        self.assertTrue(self.game.is_win())
        self.assertTrue(self.game.saved())
        self.assertListEqual([(field,)], callargs)

    def test_lose(self):
        field = Field((3, 3), {(0, 0), (2, 0)})
        with patch_field_generator(field):
            self.game.new_game((3, 3), 2)

        callargs = []
        self.game.add_event_handler(EventTypes.PLAYER_LOSE,
                                    lambda *args: callargs.append(args))

        self.game.open_cell((1, 2))
        self.assertFalse(self.game.is_lose())
        self.assertListEqual([], callargs)

        self.game.open_cell((0, 0))
        self.assertTrue(self.game.is_lose())
        self.assertTrue(self.game.saved())
        self.assertListEqual([(field, (0, 0))], callargs)

    def test_undo_redo(self):
        with patch_field_generator(Field((4, 3), {(0, 0), (3, 1)})):
            self.game.new_game((4, 3), 2)

        callargs = []
        self.game.add_event_handler(EventTypes.CELL_CHANGED,
                                    lambda *args: callargs.append(args))

        self.assertFalse(self.game.can_undo())
        self.assertFalse(self.game.can_redo())

        self.game.undo()
        self.game.redo()
        self.assertListEqual([], callargs)

        with self.subTest('flags'):
            self.game.invert_flag((0, 0))
            self.assertTrue(self.game.can_undo())
            self.assertEqual(1, self.game.flags())

            self.game.undo()
            self.assertFalse(self.game.can_undo())
            self.assertTrue(self.game.can_redo())
            self.assertEqual(0, self.game.flags())

            self.game.invert_flag((0, 0))
            self.assertFalse(self.game.can_redo())
            self.assertEqual(1, self.game.flags())

            self.game.undo()
            self.assertEqual(0, self.game.flags())
            self.game.redo()
            self.assertEqual(1, self.game.flags())

            self.assertEqual(5, len(callargs))

        with self.subTest('again'):
            self.assertTrue(self.game.can_undo() or self.game.can_redo())
            self.game.again()
            self.assertFalse(self.game.can_undo() or self.game.can_redo())

        with self.subTest('wrong_flags'):
            self.game.open_cell((2, 2))
            self.game.invert_flag((2, 2))
            self.game.undo()
            self.assertFalse(self.game.can_undo())

        with self.subTest('open'):
            self.game.again()
            callargs = []
            self.game.open_cell((2, 0))
            self.game.open_cell((0, 2))
            self.game.open_cell((1, 0))
            self.assertTrue(self.game.can_undo())
            self.game.undo()
            self.assertEqual(CellState.UNKNOWN, callargs[-1][1])
            self.assertEqual(CellState.OPENED, callargs[-2][1])

            self.assertTrue(self.game.can_undo())
            self.game.undo()
            self.assertListEqual([CellState.UNKNOWN]*6,
                                 [x[1] for x in callargs[-6:]])

            self.game.redo()
            self.assertListEqual([CellState.OPENED]*6,
                                 [x[1] for x in callargs[-6:]])

            self.assertTrue(self.game.can_redo())
            self.game.open_cell((1, 2))

        with patch_field_generator(Field((2, 1), {(0, 0)})):
            self.game.new_game((2, 1), 1)

        with self.subTest('lose'):
            self.game.open_cell((1, 0))
            self.assertFalse(self.game.is_lose())
            self.game.open_cell((0, 0))
            self.assertTrue(self.game.is_lose())
            self.assertFalse(self.game.can_undo() or self.game.can_redo())

        with self.subTest('win'):
            self.game.again()
            self.game.open_cell((1, 0))
            self.assertFalse(self.game.is_win())
            self.game.invert_flag((0, 0))
            self.assertTrue(self.game.is_win())
            self.assertFalse(self.game.can_undo() or self.game.can_redo())

    @unittest.skipIf(sys.platform.startswith('win'), 'Windows not supported')
    def test_load_bad(self):
        with tempfile.NamedTemporaryFile(delete=False) as f:
            name = f.name
            f.write(b'BAD DATA')

        old_mode = stat.S_IMODE(os.stat(name).st_mode)

        try:
            with self.assertRaises(driver.LoadError):
                self.game.load_game(name)

            self.game.new_game((4, 4), 3)
            self.game.save_game(name)
            self.assertTrue(os.path.exists(name))
            os.chmod(name, 0)
            with self.assertRaises(driver.LoadError):
                self.game.load_game(name)

            self.game.new_game((4, 4), 3)
            self.game.invert_flag((0, 0))

            os.remove(name)
            self.assertFalse(os.path.exists(name))
            with self.assertRaises(driver.LoadError):
                self.game.load_game(name)

            self.assertTrue(1, self.game.flags())
        finally:
            if os.path.exists(name):
                os.chmod(name, old_mode)
                os.remove(name)

    def test_save_bad(self):
        with tempfile.NamedTemporaryFile(delete=False) as f:
            name = f.name

        old_mode = stat.S_IMODE(os.stat(name).st_mode)

        try:
            with self.assertRaises(driver.SaveError):
                self.game.save_game(name)

            self.game.new_game((4, 4), 3)
            os.chmod(name, 0)
            with self.assertRaises(driver.SaveError):
                self.game.save_game(name)
        finally:
            if os.path.exists(name):
                os.chmod(name, old_mode)
                os.remove(name)

    def test_save_load(self):
        with tempfile.NamedTemporaryFile(delete=False) as f:
            name = f.name

        try:
            with patch_field_generator(Field((4, 3), {(0, 0), (3, 1)})):
                self.game.new_game((4, 3), 2)

            self.game.open_cell((0, 2))
            self.game.invert_flag((0, 0))
            self.game.invert_flag((1, 0))
            time.sleep(0.3)
            self.game.undo()
            self.assertFalse(self.game.saved())
            self.game.save_game(name)
            self.assertTrue(self.game.saved())
            self.game.new_game((5, 5), 10)

            callargs = []
            for evt_type in (EventTypes.NEW_GAME, EventTypes.CELL_CHANGED):
                self.game.add_event_handler(
                    evt_type, lambda *args: callargs.append(args))

            self.game.load_game(name)
            self.assertEqual(((4, 3), 2), callargs[0])
            self.assertSetEqual(
                {(0, 0)}, {x[0] for x in callargs if x[1] == CellState.FLAG})
            self.assertSetEqual(
                {(x, y) for x in (0, 1, 2) for y in (1, 2)},
                {x[0] for x in callargs if x[1] == CellState.OPENED})
            self.assertSetEqual(
                {(1, 0), (2, 0), (3, 0), (3, 1), (3, 2)},
                {x[0] for x in callargs if x[1] == CellState.UNKNOWN})
            self.assertGreaterEqual(self.game.get_time(), 30)
            self.assertTrue(self.game.can_undo())
        finally:
            if os.path.exists(name):
                os.remove(name)

    def tearDown(self):
        logging.getLogger(driver.LOGGER_NAME).removeHandler(self.lh)