def test_step(self):
        command_queue = []

        available_first_moves = []
        solver = Solver(self.puzzle)
        for (row, col) in Board.SUDOKU_CELLS:
            candidates = solver.candidates(row, col)
            if len(candidates) == 1 and self.puzzle.get_cell(
                    row, col) == Board.BLANK:
                # Solver uses zero-indexed locations, so correct
                available_first_moves.append(
                    (row + 1, col + 1, candidates.pop()))

        command_queue.append('# test `step` variants')
        for i, (row, col, num) in enumerate(available_first_moves[:2]):
            command_queue.append('# try to deduce {} at ({}, {})'.format(
                num, row, col))
            box, _ = Board.box_containing_cell(row - 1, col - 1)
            box += 1
            # Alternate the command variation used each iteration
            command_queue.append('{} {}'.format(self.STEPR_CMDS[i % 2], row))
            command_queue.append(self.UNSTEP_CMD)
            command_queue.append('{} {}'.format(self.STEPC_CMDS[i % 2], col))
            command_queue.append(self.UNSTEP_CMD)
            command_queue.append('{} {}'.format(self.STEPB_CMDS[i % 2], box))
            command_queue.append(self.UNSTEP_CMD)
            command_queue.append('# set {} at ({}, {}) manually'.format(
                num, row, col))
            command_queue.append('{} {} {} {}'.format(self.STEPM_CMDS[i % 2],
                                                      row, col, num))
            command_queue.append(self.UNSTEP_CMD)
            command_queue.append('# using `stepm` ROW COL NUM alias')
            command_queue.append('{1}{0}{2}{0}{3}'.format(
                ' ' * (i % 2), row, col, num))
            command_queue.append(self.UNSTEP_CMD)

        command_queue.append('# test breaking on `step`s')
        for step_cmd, row, col in self.FIRST_MOVES:
            command_queue.append('{} {} {}'.format(self.BREAK_CMD, row, col))
            command_queue.append(step_cmd)
            command_queue.append('# breakpoint at ({}, {})'.format(row, col))
            command_queue.append(self.UNSTEP_CMD)
        command_queue.append(self.DELETE_CMD)

        command_queue.append('# test repeating `step`s')
        command_queue.append('{} 2'.format(self.STEP_CMDS[0]))
        command_queue.append('{} 12'.format(self.STEPR_CMDS[0]))
        command_queue.append('{} 12'.format(self.STEPC_CMDS[0]))
        command_queue.append('{} 12'.format(self.STEPB_CMDS[0]))
        command_queue.append('{} 8'.format(self.UNSTEP_CMD))

        command_queue.append(
            '# test passing bad arguments to various commands')
        command_queue.append('# No steps left to undo')
        command_queue.append('{} 82'.format(self.UNSTEP_CMD))
        command_queue.append('# Must be integer')
        command_queue.append('{} x'.format(self.STEP_CMDS[0]))
        command_queue.append('{} x'.format(self.UNSTEP_CMD))
        command_queue.append('{} 1 x'.format(self.STEPR_CMDS[0]))
        command_queue.append('{} 1 x'.format(self.STEPC_CMDS[0]))
        command_queue.append('{} 1 x'.format(self.STEPB_CMDS[0]))
        command_queue.append('{} x x x'.format(self.STEPM_CMDS[0]))
        command_queue.append('# Invalid row')
        command_queue.append('{} 0 1 1'.format(self.STEPM_CMDS[0]))
        command_queue.append('{} 0'.format(self.STEPR_CMDS[0]))
        command_queue.append('# Invalid column')
        command_queue.append('{} 1 0 1'.format(self.STEPM_CMDS[0]))
        command_queue.append('{} 0'.format(self.STEPC_CMDS[0]))
        command_queue.append('# Invalid box')
        command_queue.append('{} 0'.format(self.STEPB_CMDS[0]))
        command_queue.append('# Argument required')
        command_queue.append(self.STEPR_CMDS[0])
        command_queue.append(self.STEPC_CMDS[0])
        command_queue.append(self.STEPB_CMDS[0])
        command_queue.append('# Exactly three arguments required')
        command_queue.append('{}'.format(self.STEPM_CMDS[0]))
        command_queue.append('{} 1'.format(self.STEPM_CMDS[0]))
        command_queue.append('{} 1 1'.format(self.STEPM_CMDS[0]))
        command_queue.append('# Inconsistent move')
        command_queue.append(self.INCONSISTENT_MOVE)

        # Tell controller to exit
        command_queue.append('quit')

        # Run commands
        controller = SolverController(self.puzzle,
                                      command_queue=command_queue,
                                      options=self.options)
        self.redirect_output()
        controller.solve()
        self.reset_output()

        if self.compare_file is None:
            return

        self.output_file.seek(0)
        output_lines = self.output_file.read().splitlines()
        compare_lines = self.compare_file.read().splitlines()
        self.assertEqual(output_lines, compare_lines)
    def test_set(self):
        temp_solver = Solver(self.puzzle.duplicate())
        move1_row, move1_col = temp_solver.step()
        move1_box, _ = Board.box_containing_cell(move1_row, move1_col)
        move1_num = temp_solver.puzzle.get_cell(move1_row, move1_col)

        # Adjust zero-indexed locations returned by `temp_solver`
        move1_row += 1
        move1_col += 1
        move1_box += 1

        command_queue = []
        command_queue.append('# test `set` with no subcommand')
        command_queue.append(self.SET_CMD)

        command_queue.append('# test `set ascii`')
        command_queue.append('{} ascii # turn on'.format(self.SET_CMD))
        command_queue.append('print')
        command_queue.append('{} ascii # turn off'.format(self.SET_CMD))
        command_queue.append('print')

        command_queue.append('# test `set guessbreak`')
        command_queue.append(
            'step {} # right before guess'.format(self.MOVE_OF_FIRST_GUESS -
                                                  1))
        command_queue.append('check move_before_guess')
        command_queue.append('step 2')
        command_queue.append('restart move_before_guess')
        command_queue.append('{} guessbreak # turn on'.format(self.SET_CMD))
        command_queue.append('step 2')
        command_queue.append('restart')
        command_queue.append('{} guessbreak # turn off'.format(self.SET_CMD))

        command_queue.append('# test `set explainsteps`')
        command_queue.append('{} explainsteps # turn on'.format(self.SET_CMD))
        command_queue.append(
            '# make sure explanation is printed for `step`/`stepm`')
        command_queue.append('step')
        command_queue.append('stepm {} {} {}'.format(move1_row, move1_col,
                                                     move1_num))
        command_queue.append('unstep 2')
        command_queue.append(
            '# make sure explanation is printed for other `step` variants')
        command_queue.append('stepr {}'.format(move1_row))
        command_queue.append('unstep')
        command_queue.append('stepc {}'.format(move1_col))
        command_queue.append('unstep')
        command_queue.append('stepb {}'.format(move1_box))
        command_queue.append('unstep')
        command_queue.append('# make sure explanation is printed for `finish`')
        command_queue.append('break {} {} # avoid excess output'.format(
            move1_row, move1_col))
        command_queue.append('finish')
        command_queue.append('{} explainsteps # turn off'.format(self.SET_CMD))

        command_queue.append('# test `set markview`')
        command_queue.append('{} markview # turn on'.format(self.SET_CMD))
        command_queue.append('mark 1 1 9  # add some candidates')
        command_queue.append('mark 9 9 9')
        command_queue.append('step')
        command_queue.append('explain')
        command_queue.append('unstep')
        command_queue.append('{} markview # turn off'.format(self.SET_CMD))

        command_queue.append('# test `set prompt`')
        command_queue.append('set prompt sudb# ')
        command_queue.append('set prompt all spaces  taken    literally> ')
        command_queue.append('# even no argument is taken literally')
        command_queue.append('set prompt')
        command_queue.append('# reset prompt')
        command_queue.append('set prompt {}'.format(self.options.prompt))

        command_queue.append('# test `set width`')
        command_queue.append('{} width 80 # set wide'.format(self.SET_CMD))
        command_queue.append('print marks  # show wide puzzle')
        command_queue.append('help step    # show wide wrapped text')
        command_queue.append('{} width 60 # set narrow'.format(self.SET_CMD))
        command_queue.append('print marks  # show narrow puzzle')
        command_queue.append('{} ascii'.format(self.SET_CMD))
        command_queue.append('print marks  # show narrow ascii puzzle')
        command_queue.append('help step    # show narrow wrapped text')
        command_queue.append('{} width 0  # restore default'.format(
            self.SET_CMD))

        command_queue.append('# test passing bad arguments')
        command_queue.append('# Undefined set command')
        command_queue.append('{} madeup_set_command'.format(self.SET_CMD))
        command_queue.append('# Argument must be integer')
        command_queue.append('{} width x'.format(self.SET_CMD))
        command_queue.append('# Integer out of range')
        command_queue.append('{} width -1'.format(self.SET_CMD))
        command_queue.append('# One argument required')
        command_queue.append('{} width'.format(self.SET_CMD))

        command_queue.append('# test showing all current settings')
        command_queue.append('info {} # before changes'.format(self.SET_CMD))
        command_queue.append('# make changes')
        command_queue.append('{} ascii'.format(self.SET_CMD))
        command_queue.append('{} explainsteps'.format(self.SET_CMD))
        command_queue.append('{} guessbreak'.format(self.SET_CMD))
        command_queue.append('{} markview'.format(self.SET_CMD))
        command_queue.append('{} prompt (test) '.format(self.SET_CMD))
        command_queue.append('{} width 70'.format(self.SET_CMD))
        command_queue.append('info {} # after changes'.format(self.SET_CMD))

        # Tell controller to exit
        command_queue.append('quit')

        # Run commands
        controller = SolverController(self.puzzle,
                                      command_queue=command_queue,
                                      options=self.options)
        self.redirect_output()
        controller.solve()
        self.reset_output()

        if self.compare_file is None:
            return

        self.output_file.seek(0)
        output_lines = self.output_file.read().splitlines()
        compare_lines = self.compare_file.read().splitlines()
        self.assertEqual(output_lines, compare_lines)