class EventNotification_test(object):
    def setUp(self):
        options = dict(user_options_store.DefaultOptions())
        options.update(DEFAULT_CLIENT_OPTIONS)
        user_options_store.SetAll(options)

        self.server_state = YouCompleteMe(user_options_store.GetAll())
        pass

    def tearDown(self):
        if self.server_state:
            self.server_state.OnVimLeave()

    @patch('vim.command', new_callable=ExtendedMock)
    def FileReadyToParse_NonDiagnostic_Error_test(self, vim_command):
        # This test validates the behaviour of YouCompleteMe.HandleFileParseRequest
        # in combination with YouCompleteMe.OnFileReadyToParse when the completer
        # raises an exception handling FileReadyToParse event notification
        ERROR_TEXT = 'Some completer response text'

        def ErrorResponse(*args):
            raise RuntimeError(ERROR_TEXT)

        with MockArbitraryBuffer('javascript'):
            with MockEventNotification(ErrorResponse):
                self.server_state.OnFileReadyToParse()
                assert self.server_state.FileParseRequestReady()
                self.server_state.HandleFileParseRequest()

                # The first call raises a warning
                vim_command.assert_has_exact_calls([
                    PostVimMessage_Call(ERROR_TEXT),
                ])

                # Subsequent calls don't re-raise the warning
                self.server_state.HandleFileParseRequest()
                vim_command.assert_has_exact_calls([
                    PostVimMessage_Call(ERROR_TEXT),
                ])

                # But it does if a subsequent event raises again
                self.server_state.OnFileReadyToParse()
                assert self.server_state.FileParseRequestReady()
                self.server_state.HandleFileParseRequest()
                vim_command.assert_has_exact_calls([
                    PostVimMessage_Call(ERROR_TEXT),
                    PostVimMessage_Call(ERROR_TEXT),
                ])

    @patch('vim.command')
    def FileReadyToParse_NonDiagnostic_Error_NonNative_test(self, vim_command):
        with MockArbitraryBuffer('javascript'):
            with MockEventNotification(None, False):
                self.server_state.OnFileReadyToParse()
                self.server_state.HandleFileParseRequest()
                vim_command.assert_not_called()

    @patch('ycm.client.event_notification._LoadExtraConfFile',
           new_callable=ExtendedMock)
    @patch('ycm.client.event_notification._IgnoreExtraConfFile',
           new_callable=ExtendedMock)
    def FileReadyToParse_NonDiagnostic_ConfirmExtraConf_test(
            self, ignore_extra_conf, load_extra_conf, *args):

        # This test validates the behaviour of YouCompleteMe.HandleFileParseRequest
        # in combination with YouCompleteMe.OnFileReadyToParse when the completer
        # raises the (special) UnknownExtraConf exception

        FILE_NAME = 'a_file'
        MESSAGE = ('Found ' + FILE_NAME + '. Load? \n\n(Question can be '
                   'turned off with options, see YCM docs)')

        def UnknownExtraConfResponse(*args):
            raise UnknownExtraConf(FILE_NAME)

        with MockArbitraryBuffer('javascript'):
            with MockEventNotification(UnknownExtraConfResponse):

                # When the user accepts the extra conf, we load it
                with patch('ycm.vimsupport.PresentDialog',
                           return_value=0,
                           new_callable=ExtendedMock) as present_dialog:
                    self.server_state.OnFileReadyToParse()
                    assert self.server_state.FileParseRequestReady()
                    self.server_state.HandleFileParseRequest()

                    present_dialog.assert_has_exact_calls([
                        PresentDialog_Confirm_Call(MESSAGE),
                    ])
                    load_extra_conf.assert_has_exact_calls([
                        call(FILE_NAME),
                    ])

                    # Subsequent calls don't re-raise the warning
                    self.server_state.HandleFileParseRequest()

                    present_dialog.assert_has_exact_calls(
                        [PresentDialog_Confirm_Call(MESSAGE)])
                    load_extra_conf.assert_has_exact_calls([
                        call(FILE_NAME),
                    ])

                    # But it does if a subsequent event raises again
                    self.server_state.OnFileReadyToParse()
                    assert self.server_state.FileParseRequestReady()
                    self.server_state.HandleFileParseRequest()

                    present_dialog.assert_has_exact_calls([
                        PresentDialog_Confirm_Call(MESSAGE),
                        PresentDialog_Confirm_Call(MESSAGE),
                    ])
                    load_extra_conf.assert_has_exact_calls([
                        call(FILE_NAME),
                        call(FILE_NAME),
                    ])

                # When the user rejects the extra conf, we reject it
                with patch('ycm.vimsupport.PresentDialog',
                           return_value=1,
                           new_callable=ExtendedMock) as present_dialog:
                    self.server_state.OnFileReadyToParse()
                    assert self.server_state.FileParseRequestReady()
                    self.server_state.HandleFileParseRequest()

                    present_dialog.assert_has_exact_calls([
                        PresentDialog_Confirm_Call(MESSAGE),
                    ])
                    ignore_extra_conf.assert_has_exact_calls([
                        call(FILE_NAME),
                    ])

                    # Subsequent calls don't re-raise the warning
                    self.server_state.HandleFileParseRequest()

                    present_dialog.assert_has_exact_calls(
                        [PresentDialog_Confirm_Call(MESSAGE)])
                    ignore_extra_conf.assert_has_exact_calls([
                        call(FILE_NAME),
                    ])

                    # But it does if a subsequent event raises again
                    self.server_state.OnFileReadyToParse()
                    assert self.server_state.FileParseRequestReady()
                    self.server_state.HandleFileParseRequest()

                    present_dialog.assert_has_exact_calls([
                        PresentDialog_Confirm_Call(MESSAGE),
                        PresentDialog_Confirm_Call(MESSAGE),
                    ])
                    ignore_extra_conf.assert_has_exact_calls([
                        call(FILE_NAME),
                        call(FILE_NAME),
                    ])

    def FileReadyToParse_Diagnostic_Error_Native_test(self):
        self._Check_FileReadyToParse_Diagnostic_Error()
        self._Check_FileReadyToParse_Diagnostic_Warning()
        self._Check_FileReadyToParse_Diagnostic_Clean()

    @patch('vim.command')
    def _Check_FileReadyToParse_Diagnostic_Error(self, vim_command):
        # Tests Vim sign placement and error/warning count python API
        # when one error is returned.
        def DiagnosticResponse(*args):
            start = Location(1, 2, 'TEST_BUFFER')
            end = Location(1, 4, 'TEST_BUFFER')
            extent = Range(start, end)
            diagnostic = Diagnostic([], start, extent, 'expected ;', 'ERROR')
            return [BuildDiagnosticData(diagnostic)]

        with MockArbitraryBuffer('cpp'):
            with MockEventNotification(DiagnosticResponse):
                self.server_state.OnFileReadyToParse()
                ok_(self.server_state.FileParseRequestReady())
                self.server_state.HandleFileParseRequest()
                vim_command.assert_has_calls([PlaceSign_Call(1, 1, 0, True)])
                eq_(self.server_state.GetErrorCount(), 1)
                eq_(self.server_state.GetWarningCount(), 0)

                # Consequent calls to HandleFileParseRequest shouldn't mess with
                # existing diagnostics, when there is no new parse request.
                vim_command.reset_mock()
                ok_(not self.server_state.FileParseRequestReady())
                self.server_state.HandleFileParseRequest()
                vim_command.assert_not_called()
                eq_(self.server_state.GetErrorCount(), 1)
                eq_(self.server_state.GetWarningCount(), 0)

    @patch('vim.command')
    def _Check_FileReadyToParse_Diagnostic_Warning(self, vim_command):
        # Tests Vim sign placement/unplacement and error/warning count python API
        # when one warning is returned.
        # Should be called after _Check_FileReadyToParse_Diagnostic_Error
        def DiagnosticResponse(*args):
            start = Location(2, 2, 'TEST_BUFFER')
            end = Location(2, 4, 'TEST_BUFFER')
            extent = Range(start, end)
            diagnostic = Diagnostic([], start, extent, 'cast', 'WARNING')
            return [BuildDiagnosticData(diagnostic)]

        with MockArbitraryBuffer('cpp'):
            with MockEventNotification(DiagnosticResponse):
                self.server_state.OnFileReadyToParse()
                ok_(self.server_state.FileParseRequestReady())
                self.server_state.HandleFileParseRequest()
                vim_command.assert_has_calls(
                    [PlaceSign_Call(2, 2, 0, False),
                     UnplaceSign_Call(1, 0)])
                eq_(self.server_state.GetErrorCount(), 0)
                eq_(self.server_state.GetWarningCount(), 1)

                # Consequent calls to HandleFileParseRequest shouldn't mess with
                # existing diagnostics, when there is no new parse request.
                vim_command.reset_mock()
                ok_(not self.server_state.FileParseRequestReady())
                self.server_state.HandleFileParseRequest()
                vim_command.assert_not_called()
                eq_(self.server_state.GetErrorCount(), 0)
                eq_(self.server_state.GetWarningCount(), 1)

    @patch('vim.command')
    def _Check_FileReadyToParse_Diagnostic_Clean(self, vim_command):
        # Tests Vim sign unplacement and error/warning count python API
        # when there are no errors/warnings left.
        # Should be called after _Check_FileReadyToParse_Diagnostic_Warning
        with MockArbitraryBuffer('cpp'):
            with MockEventNotification(MagicMock(return_value=[])):
                self.server_state.OnFileReadyToParse()
                self.server_state.HandleFileParseRequest()
                vim_command.assert_has_calls([UnplaceSign_Call(2, 0)])
                eq_(self.server_state.GetErrorCount(), 0)
                eq_(self.server_state.GetWarningCount(), 0)