def test_immediate_polling_with_local_clock_unsynced(self):
        """First poll happens with minimal delay if local clock is way off from
        the remote/server clock."""

        # each thread can have its instance of a session because
        # the mocked responses are stateless
        def create_mock_session(client):
            badnow = utcrel(100)
            session = mock.Mock()
            session.post = lambda path, _: choose_reply(
                path, {'problems/': [self.sapi.continue_reply(id='1')]},
                date=badnow)
            session.get = lambda path: choose_reply(path, {
                'problems/?id=1': [self.sapi.complete_no_answer_reply(id='1')],
                'problems/1/':
                self.sapi.complete_reply(id='1')
            },
                                                    date=badnow)
            return session

        with mock.patch.object(Client, 'create_session', create_mock_session):
            with Client('endpoint', 'token') as client:
                solver = Solver(client, self.sapi.solver.data)

                def assert_no_delay(s):
                    s and self.assertTrue(
                        abs(s - client.poll_backoff_min) <
                        client.DEFAULTS['poll_backoff_min'])

                with mock.patch('time.sleep', assert_no_delay):
                    future = solver.sample_qubo({})
                    future.result()
    def test_immediate_polling(self):
        "First poll happens with minimal delay"

        # each thread can have its instance of a session because
        # responses are stateless
        def create_mock_session(client):
            session = mock.Mock()
            session.post = lambda path, _: choose_reply(
                path, {'problems/': [self.sapi.continue_reply(id='1')]})
            session.get = lambda path: choose_reply(
                path, {
                    'problems/?id=1':
                    [self.sapi.complete_no_answer_reply(id='1')],
                    'problems/1/': self.sapi.complete_reply(id='1')
                })
            return session

        with mock.patch.object(Client, 'create_session', create_mock_session):
            with Client(endpoint='endpoint', token='token') as client:
                solver = Solver(client, self.sapi.solver.data)

                def assert_no_delay(s):
                    s and self.assertTrue(
                        abs(s - client.poll_backoff_min) <
                        client.DEFAULTS['poll_backoff_min'])

                with mock.patch('time.sleep', assert_no_delay):
                    future = solver.sample_qubo({})
                    future.result()
    def test_polling_recovery_after_5xx(self):
        "Polling shouldn't be aborted on 5xx responses."

        # we need a "global session", because mocked responses are stateful
        def global_mock_session():
            session = mock.Mock()

            # on submit, return status pending
            session.post = lambda path, _: choose_reply(
                path, {'problems/': [self.sapi.continue_reply(id='123')]})

            # on first and second status poll, fail with 503 and 504
            # on third status poll, return completed
            statuses = iter([503, 504])

            def continue_then_complete(path, state={'count': 0}):
                state['count'] += 1
                if state['count'] < 3:
                    return choose_reply(
                        path,
                        replies={
                            'problems/?id=123':
                            [self.sapi.continue_reply(id='123')],
                            'problems/123/':
                            self.sapi.continue_reply(id='123')
                        },
                        statuses={
                            'problems/?id=123': statuses,
                            'problems/123/': statuses
                        })
                else:
                    return choose_reply(
                        path, {
                            'problems/?id=123':
                            [self.sapi.complete_no_answer_reply(id='123')],
                            'problems/123/':
                            self.sapi.complete_reply(id='123')
                        })

            session.get = continue_then_complete

            return session

        session = global_mock_session()

        with mock.patch.object(Client, 'create_session', lambda self: session):
            with Client(endpoint='endpoint', token='token') as client:
                solver = Solver(client, self.sapi.solver.data)

                future = solver.sample_qubo({})
                future.result()

                # after third poll, back-off interval should be 4 x initial back-off
                self.assertAlmostEqual(
                    future._poll_backoff,
                    client.poll_backoff_min * client.poll_backoff_base**2)
    def test_exponential_backoff_polling(self):
        "After each poll, back-off should double"

        # we need a "global session", because mocked responses are stateful
        def global_mock_session():
            session = mock.Mock()

            # on submit, return status pending
            session.post = lambda path, _: choose_reply(
                path, {'problems/': [self.sapi.continue_reply(id='123')]})

            # on first and second status poll, return pending
            # on third status poll, return completed
            def continue_then_complete(path, state={'count': 0}):
                state['count'] += 1
                if state['count'] < 3:
                    return choose_reply(
                        path, {
                            'problems/?id=123':
                            [self.sapi.continue_reply(id='123')],
                            'problems/123/':
                            self.sapi.continue_reply(id='123')
                        })
                else:
                    return choose_reply(
                        path, {
                            'problems/?id=123':
                            [self.sapi.complete_no_answer_reply(id='123')],
                            'problems/123/':
                            self.sapi.complete_reply(id='123')
                        })

            session.get = continue_then_complete

            return session

        session = global_mock_session()

        with mock.patch.object(Client, 'create_session', lambda self: session):
            with Client('endpoint', 'token') as client:
                solver = Solver(client, self.sapi.solver.data)

                future = solver.sample_qubo({})
                future.result()

                # after third poll, back-off interval should be 4 x initial back-off
                self.assertEqual(future._poll_backoff,
                                 client.poll_backoff_min * 2**2)
    def test_submit_bqm_qubo_ok_reply(self):
        """Handle a normal query and response."""

        qubo_msg_diff = dict(type="qubo")
        qubo_answer_diff = {
            'energies': 'AAAAAAAAAAA=',
            'solutions': 'AA==',
            'active_variables': 'AAAAAAQAAAA='
        }

        # each thread can have its instance of a session because
        # the mocked responses are stateless
        def create_mock_session(client):
            session = mock.Mock()
            session.post = lambda a, _: choose_reply(a, {
                'problems/': [self.sapi.complete_no_answer_reply(id='123')]
            })
            session.get = lambda a: choose_reply(
                a, {
                    'problems/123/':
                    self.sapi.complete_reply(id='123',
                                             answer_patch=qubo_answer_diff,
                                             **qubo_msg_diff)
                })
            return session

        with mock.patch.object(Client, 'create_session', create_mock_session):
            with Client('endpoint', 'token') as client:
                solver = Solver(client, self.sapi.solver.data)

                qubo = {(0, 0): 4.0, (0, 4): -4, (4, 4): 4.0}
                offset = -2.0
                params = dict(num_reads=100)

                results = solver.sample_qubo(qubo, offset, **params)

                # make sure energies are correct in raw results
                for energy, sample in zip(results.energies, results.samples):
                    self.assertEqual(
                        energy, evaluate_ising({}, qubo, sample,
                                               offset=offset))