def test_eta_min_is_ignored_on_first_poll(self): "eta_min/earliest_estimated_completion should not be used anymore" with Client('endpoint', 'token') as client: now = datetime_in_future(0) eta_min, eta_max = datetime_in_future(10), datetime_in_future(30) client.session = mock.Mock() client.session.post = lambda path, _: choose_reply(path, { 'endpoint/problems/': '[%s]' % continue_reply( '1', 'abc123', eta_min=eta_min, eta_max=eta_max, now=now) }, date=now) client.session.get = lambda path: choose_reply(path, { 'endpoint/problems/?id=1': '[%s]' % complete_no_answer_reply('1', 'abc123'), 'endpoint/problems/1/': complete_reply('1', 'abc123') }, date=now) solver = Solver(client, solver_data('abc123')) def assert_no_delay(s): s and self.assertTrue( abs(s - client._POLL_BACKOFF_MIN) < client._POLL_BACKOFF_MIN / 10.0) with mock.patch('time.sleep', assert_no_delay): future = solver.sample_qubo({}) future.result()
def test_upload_failure(self): """Submit should gracefully fail if upload as part of submit fails.""" # build a test problem bqm = dimod.BQM.from_ising({}, {'ab': 1}) # use a global mocked session, so we can modify it on-fly session = mock.Mock() # upload is now part of submit, so we need to mock it mock_upload_exc = ValueError('error') def mock_upload(self, bqm): return Present(exception=mock_upload_exc) # construct a functional solver by mocking client and api response data with mock.patch.object(Client, 'create_session', lambda self: session): with Client('endpoint', 'token') as client: with mock.patch.object(UnstructuredSolver, 'upload_bqm', mock_upload): solver = UnstructuredSolver(client, unstructured_solver_data()) # direct bqm sampling ss = dimod.ExactSolver().sample(bqm) session.post = lambda path, _: choose_reply( path, {'problems/': complete_reply(ss)}) fut = solver.sample_bqm(bqm) with self.assertRaises(type(mock_upload_exc)): fut.result()
def test_many_upload_failures(self): """Failure handling in high concurrency mode works correctly.""" # build a test problem bqm = dimod.BQM.from_ising({}, {'ab': 1}) # use a global mocked session, so we can modify it on-fly session = mock.Mock() # upload is now part of submit, so we need to mock it mock_upload_exc = ValueError('error') def mock_upload(self, bqm): return Present(exception=mock_upload_exc) # construct a functional solver by mocking client and api response data with mock.patch.object(Client, 'create_session', lambda self: session): with Client('endpoint', 'token') as client: with mock.patch.object(UnstructuredSolver, 'upload_bqm', mock_upload): solver = UnstructuredSolver(client, unstructured_solver_data()) futs = [solver.sample_bqm(bqm) for _ in range(100)] for fut in futs: with self.assertRaises(type(mock_upload_exc)): fut.result()
def test_cancel_with_id(self): """Make sure the cancel method submits to the right endpoint. When cancel is called after the submission is finished. """ submission_id = 'test-id' reply_body = '[%s]' % continue_reply(submission_id, 'solver') with Client('endpoint', 'token') as client: client.session = mock.Mock() client.session.get = lambda a: choose_reply( a, {'endpoint/problems/?id={}'.format(submission_id): reply_body}) client.session.delete = DeleteEvent.handle solver = Solver(client, solver_data('abc123')) future = solver.retrieve_problem(submission_id) future.cancel() try: self.assertTrue(future.id is not None) future.samples self.fail() except DeleteEvent as event: if event.url == 'endpoint/problems/': self.assertEqual(event.body, '["{}"]'.format(submission_id)) else: self.assertEqual( event.url, 'endpoint/problems/{}/'.format(submission_id))
def test_submit_continue_then_ok_reply(self): """Handle polling for a complete problem.""" with Client('endpoint', 'token') as client: now = datetime_in_future(0) eta_min, eta_max = datetime_in_future(10), datetime_in_future(30) client.session = mock.Mock() client.session.post = lambda a, _: choose_reply(a, { 'endpoint/problems/': '[%s]' % continue_reply( '123', 'abc123', eta_min=eta_min, eta_max=eta_max, now=now) }, date=now) client.session.get = lambda a: choose_reply(a, { 'endpoint/problems/?id=123': '[%s]' % complete_no_answer_reply('123', 'abc123'), 'endpoint/problems/123/': complete_reply('123', 'abc123') }, date=now) solver = Solver(client, solver_data('abc123')) # Build a problem linear = {index: 1 for index in solver.nodes} quad = {key: -1 for key in solver.undirected_edges} results = solver.sample_ising(linear, quad, num_reads=100) self._check(results, linear, quad, 100) # test future has eta_min and eta_max parsed correctly self.assertEqual(results.eta_min, eta_min) self.assertEqual(results.eta_max, eta_max)
def test_exponential_backoff_polling(self): "After each poll, back-off should double" with Client('endpoint', 'token') as client: client.session = mock.Mock() # on submit, return status pending client.session.post = lambda path, _: choose_reply(path, { 'endpoint/problems/': '[%s]' % continue_reply('123', 'abc123') }) # 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, { 'endpoint/problems/?id=123': '[%s]' % continue_reply('123', 'abc123'), 'endpoint/problems/123/': continue_reply('123', 'abc123') }) else: return choose_reply(path, { 'endpoint/problems/?id=123': '[%s]' % complete_no_answer_reply('123', 'abc123'), 'endpoint/problems/123/': complete_reply('123', 'abc123') }) client.session.get = continue_then_complete solver = Solver(client, solver_data('abc123')) 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 global_mock_session(): session = mock.Mock() # on submit, return status pending session.post = lambda path, _: choose_reply( path, {'problems/': '[%s]' % continue_reply('123', 'abc123')}) # 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': '[%s]' % continue_reply('123', 'abc123'), 'problems/123/': continue_reply('123', 'abc123') }) else: return choose_reply( path, { 'problems/?id=123': '[%s]' % complete_no_answer_reply('123', 'abc123'), 'problems/123/': complete_reply('123', 'abc123') }) session.get = continue_then_complete return session
def create_mock_session(client): session = mock.Mock() reply_body = '[%s]' % continue_reply(submission_id, 'solver') session.get = lambda a: choose_reply( a, {'problems/?id={}'.format(submission_id): reply_body}) session.delete = DeleteEvent.handle return session
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.""" with Client('endpoint', 'token') as client: badnow = datetime_in_future(100) client.session = mock.Mock() client.session.post = lambda path, _: choose_reply( path, {'endpoint/problems/': '[%s]' % continue_reply('1', 'abc123')}, date=badnow) client.session.get = lambda path: choose_reply(path, { 'endpoint/problems/?id=1': '[%s]' % complete_no_answer_reply('1', 'abc123'), 'endpoint/problems/1/': complete_reply('1', 'abc123') }, date=badnow) solver = Solver(client, solver_data('abc123')) def assert_no_delay(s): s and self.assertTrue( abs(s - client._POLL_BACKOFF_MIN) < client._POLL_BACKOFF_MIN / 10.0) with mock.patch('time.sleep', assert_no_delay): future = solver.sample_qubo({}) future.result()
def create_mock_session(client): session = mock.Mock() session.post = lambda a, _: choose_reply( a, { 'problems/': '[%s]' % error_reply('123', 'abc123', 'An error message') }) return session
def create_mock_session(client): session = mock.Mock() session.post = lambda a, _: choose_reply(a, { 'problems/': '[%s]' % complete_no_answer_reply('123', 'abc123') }) session.get = lambda a: choose_reply( a, {'problems/123/': complete_reply('123', 'abc123')}) return session
def create_mock_session(client): session = mock.Mock() session.post = lambda a, _: choose_reply( a, { 'problems/': '[%s]' % immediate_error_reply( 400, "Missing parameter 'num_reads' in problem JSON") }) return session
def choose_reply(path, replies): """Choose the right response based on the path and make a mock response.""" if path in replies: response = mock.Mock(['json', 'raise_for_status']) response.status_code = 200 response.json.side_effect = lambda: json.loads(replies[path]) return response else: raise NotImplementedError(path)
def create_mock_session(client): session = mock.Mock() session.post = lambda a, _: choose_reply( a, {'problems/': '[%s]' % continue_reply('123', 'abc123')}) session.get = lambda a: choose_reply( a, { 'problems/?id=123': '[%s]' % error_reply('123', 'abc123', "error message") }) return session
def test_submit_continue_then_ok_and_error_reply(self): """Handle polling for the status of multiple problems.""" with Client('endpoint', 'token') as client: client.session = mock.Mock() # on first status poll, return pending for both problems # on second status poll, return error for first problem and complete for second def continue_then_complete(path, state={'count': 0}): state['count'] += 1 if state['count'] < 2: return choose_reply(path, { 'endpoint/problems/?id=1': '[{}]'.format(continue_reply('1', 'abc123')), 'endpoint/problems/?id=2': '[{}]'.format(continue_reply('2', 'abc123')), 'endpoint/problems/1/': continue_reply('1', 'abc123'), 'endpoint/problems/2/': continue_reply('2', 'abc123'), 'endpoint/problems/?id=1,2': '[{},{}]'.format(continue_reply('1', 'abc123'), continue_reply('2', 'abc123')), 'endpoint/problems/?id=2,1': '[{},{}]'.format(continue_reply('2', 'abc123'), continue_reply('1', 'abc123')) }) else: return choose_reply(path, { 'endpoint/problems/?id=1': '[{}]'.format(error_reply('1', 'abc123', 'error')), 'endpoint/problems/?id=2': '[{}]'.format(complete_no_answer_reply('2', 'abc123')), 'endpoint/problems/1/': error_reply('1', 'abc123', 'error'), 'endpoint/problems/2/': complete_reply('2', 'abc123'), 'endpoint/problems/?id=1,2': '[{},{}]'.format(error_reply('1', 'abc123', 'error'), complete_no_answer_reply('2', 'abc123')), 'endpoint/problems/?id=2,1': '[{},{}]'.format(complete_no_answer_reply('2', 'abc123'), error_reply('1', 'abc123', 'error')) }) client.session.get = continue_then_complete def accept_problems_with_continue_reply(path, body, ids=iter('12')): problems = json.loads(body) return choose_reply(path, { 'endpoint/problems/': json.dumps( [json.loads(continue_reply(next(ids), 'abc123')) for _ in problems]) }) client.session.post = accept_problems_with_continue_reply solver = Solver(client, solver_data('abc123')) linear = {index: 1 for index in solver.nodes} quad = {key: -1 for key in solver.undirected_edges} results1 = solver.sample_ising(linear, quad, num_reads=100) results2 = solver.sample_ising(linear, quad, num_reads=100) with self.assertRaises(SolverFailureError): self._check(results1, linear, quad, 100) self._check(results2, linear, quad, 100)
def test_submit_immediate_reply(self): """Construction of and sampling from an unstructured solver works.""" # build a test problem bqm = dimod.BQM.from_ising({}, {'ab': 1}) # use a global mocked session, so we can modify it on-fly session = mock.Mock() # construct a functional solver by mocking client and api response data with mock.patch.object(Client, 'create_session', lambda self: session): with Client('endpoint', 'token') as client: solver = UnstructuredSolver(client, unstructured_solver_data()) # direct bqm sampling ss = dimod.ExactSolver().sample(bqm) session.post = lambda path, _: choose_reply( path, {'problems/': complete_reply(ss)}) fut = solver.sample_bqm(bqm) numpy.testing.assert_array_equal(fut.sampleset, ss) numpy.testing.assert_array_equal(fut.samples, ss.record.sample) numpy.testing.assert_array_equal(fut.energies, ss.record.energy) numpy.testing.assert_array_equal(fut.occurrences, ss.record.num_occurrences) # ising sampling lin, quad, _ = bqm.to_ising() ss = dimod.ExactSolver().sample_ising(lin, quad) session.post = lambda path, _: choose_reply( path, {'problems/': complete_reply(ss)}) fut = solver.sample_ising(lin, quad) numpy.testing.assert_array_equal(fut.sampleset, ss) numpy.testing.assert_array_equal(fut.samples, ss.record.sample) numpy.testing.assert_array_equal(fut.energies, ss.record.energy) numpy.testing.assert_array_equal(fut.occurrences, ss.record.num_occurrences) # qubo sampling qubo, _ = bqm.to_qubo() ss = dimod.ExactSolver().sample_qubo(qubo) session.post = lambda path, _: choose_reply( path, {'problems/': complete_reply(ss)}) fut = solver.sample_qubo(qubo) numpy.testing.assert_array_equal(fut.sampleset, ss) numpy.testing.assert_array_equal(fut.samples, ss.record.sample) numpy.testing.assert_array_equal(fut.energies, ss.record.energy) numpy.testing.assert_array_equal(fut.occurrences, ss.record.num_occurrences)
def create_mock_session(client): session = mock.Mock() # delayed submit; emulates waiting in queue def post(path, _): release_reply.wait() reply_body = complete_reply(submission_id, solver_name) return choose_reply(path, {'problems/': '[%s]' % reply_body}) session.post = post return session
def choose_reply(path, replies, date=None): """Choose the right response based on the path and make a mock response.""" if date is None: date = datetime_in_future(0) if path in replies: response = mock.Mock(['json', 'raise_for_status', 'headers']) response.status_code = 200 response.json.side_effect = lambda: json.loads(replies[path]) response.headers = CaseInsensitiveDict({'Date': date.isoformat()}) return response else: raise NotImplementedError(path)
def test_func_called_only_until_succeeds(self): """Wrapped function is called no more times then it takes to succeed.""" err = ValueError val = mock.sentinel attrs = dict(__name__='f') # f succeeds on 3rd try f = mock.Mock(side_effect=[err, err, val.a, val.b], **attrs) ret = retried(3)(f)() self.assertEqual(ret, val.a) self.assertEqual(f.call_count, 3) # fail with only on retry f = mock.Mock(side_effect=[err, err, val.a, val.b], **attrs) with self.assertRaises(err): ret = retried(1)(f)() # no errors, return without retries f = mock.Mock(side_effect=[val.a, val.b, val.c], **attrs) ret = retried(3)(f)() self.assertEqual(ret, val.a) self.assertEqual(f.call_count, 1)
def create_mock_session(client): badnow = datetime_in_future(100) session = mock.Mock() session.post = lambda path, _: choose_reply( path, {'problems/': '[%s]' % continue_reply('1', 'abc123')}, date=badnow) session.get = lambda path: choose_reply(path, { 'problems/?id=1': '[%s]' % complete_no_answer_reply('1', 'abc123'), 'problems/1/': complete_reply('1', 'abc123') }, date=badnow) return session
def test_submit_cancel_reply(self): """Handle a response for a canceled job.""" with Client('endpoint', 'token') as client: client.session = mock.Mock() client.session.post = lambda a, _: choose_reply(a, {'endpoint/problems/': '[%s]' % cancel_reply('123', 'abc123')}) solver = Solver(client, solver_data('abc123')) # Build a problem linear = {index: 1 for index in solver.nodes} quad = {key: -1 for key in solver.undirected_edges} results = solver.sample_ising(linear, quad, num_reads=100) with self.assertRaises(CanceledFutureError): results.samples
def test_submit_null_reply(self): """Get an error when the server's response is incomplete.""" with Client('endpoint', 'token') as client: client.session = mock.Mock() client.session.post = lambda a, _: choose_reply(a, {'endpoint/problems/': ''}) solver = Solver(client, solver_data('abc123')) # Build a problem linear = {index: 1 for index in solver.nodes} quad = {key: -1 for key in solver.undirected_edges} results = solver.sample_ising(linear, quad, num_reads=100) with self.assertRaises(ValueError): results.samples
def test_polling_recovery_after_5xx(self): "Polling shouldn't be aborted on 5xx responses." with Client('endpoint', 'token') as client: client.session = mock.Mock() # on submit, return status pending client.session.post = lambda path, _: choose_reply( path, { 'endpoint/problems/': '[%s]' % continue_reply( '123', 'abc123') }) # 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={ 'endpoint/problems/?id=123': '[%s]' % continue_reply('123', 'abc123'), 'endpoint/problems/123/': continue_reply('123', 'abc123') }, statuses={ 'endpoint/problems/?id=123': statuses, 'endpoint/problems/123/': statuses }) else: return choose_reply( path, { 'endpoint/problems/?id=123': '[%s]' % complete_no_answer_reply('123', 'abc123'), 'endpoint/problems/123/': complete_reply('123', 'abc123') }) client.session.get = continue_then_complete solver = Solver(client, solver_data('abc123')) 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_ok_reply(self): """Handle a normal query and response.""" with Client('endpoint', 'token') as client: client.session = mock.Mock() client.session.post = lambda a, _: choose_reply(a, { 'endpoint/problems/': '[%s]' % complete_no_answer_reply('123', 'abc123')}) client.session.get = lambda a: choose_reply(a, {'endpoint/problems/123/': complete_reply('123', 'abc123')}) solver = Solver(client, solver_data('abc123')) # Build a problem linear = {index: 1 for index in solver.nodes} quad = {key: -1 for key in solver.undirected_edges} results = solver.sample_ising(linear, quad, num_reads=100) self._check(results, linear, quad, 100)
def test_submit_error_reply(self): """Handle an error on problem submission.""" error_body = 'An error message' with Client('endpoint', 'token') as client: client.session = mock.Mock() client.session.post = lambda a, _: choose_reply(a, { 'endpoint/problems/': '[%s]' % error_reply('123', 'abc123', error_body)}) solver = Solver(client, solver_data('abc123')) # Build a problem linear = {index: 1 for index in solver.nodes} quad = {key: -1 for key in solver.undirected_edges} results = solver.sample_ising(linear, quad, num_reads=100) with self.assertRaises(SolverFailureError): results.samples
def create_mock_session(client): session = mock.Mock() session.post = lambda a, _: choose_reply(a, { 'problems/': '[%s]' % continue_reply( '123', 'abc123', eta_min=eta_min, eta_max=eta_max, now=now) }, date=now) session.get = lambda a: choose_reply(a, { 'problems/?id=123': '[%s]' % complete_no_answer_reply('123', 'abc123'), 'problems/123/': complete_reply('123', 'abc123') }, date=now) return session
def test_submit_continue_then_error_reply(self): """Handle polling for an error message.""" with Client('endpoint', 'token') as client: client.session = mock.Mock() client.session.post = lambda a, _: choose_reply(a, {'endpoint/problems/': '[%s]' % continue_reply('123', 'abc123')}) client.session.get = lambda a: choose_reply(a, { 'endpoint/problems/?id=123': '[%s]' % error_reply('123', 'abc123', "error message")}) solver = Solver(client, solver_data('abc123')) # Build a problem linear = {index: 1 for index in solver.nodes} quad = {key: -1 for key in solver.undirected_edges} results = solver.sample_ising(linear, quad, num_reads=100) with self.assertRaises(SolverFailureError): self._check(results, linear, quad, 100)
def test_submit_immediate_error_reply(self): """Handle an (obvious) error on problem submission.""" with Client('endpoint', 'token') as client: client.session = mock.Mock() client.session.post = lambda a, _: choose_reply( a, { 'endpoint/problems/': '[%s]' % immediate_error_reply( 400, "Missing parameter 'num_reads' in problem JSON") }) solver = Solver(client, solver_data('abc123')) linear, quad = generate_random_ising_problem(solver) results = solver.sample_ising(linear, quad) with self.assertRaises(SolverFailureError): results.samples
def test_cancel_without_id(self): """Make sure the cancel method submits to the right endpoint. When cancel is called before the submission has returned the problem id. """ submission_id = 'test-id' reply_body = '[%s]' % continue_reply(submission_id, 'solver') release_reply = threading.Event() with Client('endpoint', 'token') as client: client.session = mock.Mock() client.session.get = lambda a: choose_reply( a, {'endpoint/problems/?id={}'.format(submission_id): reply_body}) def post(a, _): release_reply.wait() return choose_reply(a, {'endpoint/problems/': reply_body}) client.session.post = post client.session.delete = DeleteEvent.handle solver = Solver(client, solver_data('abc123')) # Build a problem linear = {index: 1 for index in solver.nodes} quad = {key: -1 for key in solver.undirected_edges} future = solver.sample_ising(linear, quad) future.cancel() try: release_reply.set() future.samples self.fail() except DeleteEvent as event: if event.url == 'endpoint/problems/': self.assertEqual(event.body, '["{}"]'.format(submission_id)) else: self.assertEqual( event.url, 'endpoint/problems/{}/'.format(submission_id))
def test_immediate_polling_without_eta_min(self): "First poll happens with minimal delay if eta_min missing" with Client('endpoint', 'token') as client: client.session = mock.Mock() client.session.post = lambda path, _: choose_reply(path, { 'endpoint/problems/': '[%s]' % continue_reply('1', 'abc123') }) client.session.get = lambda path: choose_reply(path, { 'endpoint/problems/?id=1': '[%s]' % complete_no_answer_reply('1', 'abc123'), 'endpoint/problems/1/': complete_reply('1', 'abc123') }) solver = Solver(client, solver_data('abc123')) def assert_no_delay(s): s and self.assertTrue( abs(s - client._POLL_BACKOFF_MIN) < client._POLL_BACKOFF_MIN / 10.0) with mock.patch('time.sleep', assert_no_delay): future = solver.sample_qubo({}) future.result()