def test_get_process_auth_db_exceptions(self): """Ensure get_process_auth_db() handles DB exceptions well.""" # Prepare several instances of AuthDB to be used in mocks. auth_db_v0 = api.AuthDB(entity_group_version=0) auth_db_v1 = api.AuthDB(entity_group_version=1) # Fetch initial copy of AuthDB. self.set_time(0) self.set_fetched_auth_db(auth_db_v0) self.assertEqual(auth_db_v0, api.get_process_auth_db()) # Make process cache expire. self.set_time(api.get_process_cache_expiration_sec() + 1) # Emulate an exception in fetch_auth_db. def mock_fetch_auth_db(*_kwargs): raise Exception('Boom!') self.mock(api, 'fetch_auth_db', mock_fetch_auth_db) # Capture calls to logging.exception. logger_calls = [] self.mock(api.logging, 'exception', lambda *_args: logger_calls.append(1)) # Should return older copy of auth_db_v0 and log the exception. self.assertEqual(auth_db_v0, api.get_process_auth_db()) self.assertEqual(1, len(logger_calls)) # Make fetch_auth_db to work again. Verify get_process_auth_db() works too. self.set_fetched_auth_db(auth_db_v1) self.assertEqual(auth_db_v1, api.get_process_auth_db())
def test_get_process_auth_db_exceptions(self): """Ensure get_process_auth_db() handles DB exceptions well.""" # Prepare several instances of AuthDB to be used in mocks. auth_db_v0 = api.AuthDB(entity_group_version=0) auth_db_v1 = api.AuthDB(entity_group_version=1) # Fetch initial copy of AuthDB. self.set_time(0) self.set_fetched_auth_db(auth_db_v0) self.assertEqual(auth_db_v0, api.get_process_auth_db()) # Make process cache expire. self.set_time(api.get_process_cache_expiration_sec() + 1) # Emulate an exception in fetch_auth_db. def mock_fetch_auth_db(*_kwargs): raise Exception("Boom!") self.mock(api, "fetch_auth_db", mock_fetch_auth_db) # Capture calls to logging.exception. logger_calls = [] self.mock(api.logging, "exception", lambda *_args: logger_calls.append(1)) # Should return older copy of auth_db_v0 and log the exception. self.assertEqual(auth_db_v0, api.get_process_auth_db()) self.assertEqual(1, len(logger_calls)) # Make fetch_auth_db to work again. Verify get_process_auth_db() works too. self.set_fetched_auth_db(auth_db_v1) self.assertEqual(auth_db_v1, api.get_process_auth_db())
def test_get_process_auth_db_expiration(self): """Ensure get_process_auth_db() respects expiration.""" # Prepare several instances of AuthDB to be used in mocks. auth_db_v0 = api.AuthDB(entity_group_version=0) auth_db_v1 = api.AuthDB(entity_group_version=1) # Fetch initial copy of AuthDB. self.set_time(0) self.set_fetched_auth_db(auth_db_v0) self.assertEqual(auth_db_v0, api.get_process_auth_db()) # It doesn't expire for some time. self.set_time(api.get_process_cache_expiration_sec() - 1) self.set_fetched_auth_db(auth_db_v1) self.assertEqual(auth_db_v0, api.get_process_auth_db()) # But eventually it does. self.set_time(api.get_process_cache_expiration_sec() + 1) self.set_fetched_auth_db(auth_db_v1) self.assertEqual(auth_db_v1, api.get_process_auth_db())
def test_get_process_auth_db_multithreading(self): """Ensure get_process_auth_db() plays nice with multiple threads.""" def run_in_thread(func): """Runs |func| in a parallel thread, returns future (as Queue).""" result = Queue.Queue() thread = threading.Thread(target=lambda: result.put(func())) thread.start() return result # Prepare several instances of AuthDB to be used in mocks. auth_db_v0 = api.AuthDB(entity_group_version=0) auth_db_v1 = api.AuthDB(entity_group_version=1) # Run initial fetch, should cache |auth_db_v0| in process cache. self.set_time(0) self.set_fetched_auth_db(auth_db_v0) self.assertEqual(auth_db_v0, api.get_process_auth_db()) # Make process cache expire. self.set_time(api.get_process_cache_expiration_sec() + 1) # Start fetching AuthDB from another thread, at some point it will call # 'fetch_auth_db', and we pause the thread then and resume main thread. fetching_now = threading.Event() auth_db_queue = Queue.Queue() def mock_fetch_auth_db(**_kwargs): fetching_now.set() return auth_db_queue.get() self.mock(api, "fetch_auth_db", mock_fetch_auth_db) future = run_in_thread(api.get_process_auth_db) # Wait for internal thread to call |fetch_auth_db|. fetching_now.wait() # Ok, now main thread is unblocked, while internal thread is blocking on a # artificially slow 'fetch_auth_db' call. Main thread can now try to get # AuthDB via get_process_auth_db(). It should get older stale copy right # away. self.assertEqual(auth_db_v0, api.get_process_auth_db()) # Finish background 'fetch_auth_db' call by returning 'auth_db_v1'. # That's what internal thread should get as result of 'get_process_auth_db'. auth_db_queue.put(auth_db_v1) self.assertEqual(auth_db_v1, future.get()) # Now main thread should get it as well. self.assertEqual(auth_db_v1, api.get_process_auth_db())
def test_get_process_auth_db_multithreading(self): """Ensure get_process_auth_db() plays nice with multiple threads.""" def run_in_thread(func): """Runs |func| in a parallel thread, returns future (as Queue).""" result = Queue.Queue() thread = threading.Thread(target=lambda: result.put(func())) thread.start() return result # Prepare several instances of AuthDB to be used in mocks. auth_db_v0 = api.AuthDB(entity_group_version=0) auth_db_v1 = api.AuthDB(entity_group_version=1) # Run initial fetch, should cache |auth_db_v0| in process cache. self.set_time(0) self.set_fetched_auth_db(auth_db_v0) self.assertEqual(auth_db_v0, api.get_process_auth_db()) # Make process cache expire. self.set_time(api.get_process_cache_expiration_sec() + 1) # Start fetching AuthDB from another thread, at some point it will call # 'fetch_auth_db', and we pause the thread then and resume main thread. fetching_now = threading.Event() auth_db_queue = Queue.Queue() def mock_fetch_auth_db(**_kwargs): fetching_now.set() return auth_db_queue.get() self.mock(api, 'fetch_auth_db', mock_fetch_auth_db) future = run_in_thread(api.get_process_auth_db) # Wait for internal thread to call |fetch_auth_db|. fetching_now.wait() # Ok, now main thread is unblocked, while internal thread is blocking on a # artificially slow 'fetch_auth_db' call. Main thread can now try to get # AuthDB via get_process_auth_db(). It should get older stale copy right # away. self.assertEqual(auth_db_v0, api.get_process_auth_db()) # Finish background 'fetch_auth_db' call by returning 'auth_db_v1'. # That's what internal thread should get as result of 'get_process_auth_db'. auth_db_queue.put(auth_db_v1) self.assertEqual(auth_db_v1, future.get()) # Now main thread should get it as well. self.assertEqual(auth_db_v1, api.get_process_auth_db())
def test_get_process_auth_db_known_version(self): """Ensure get_process_auth_db() respects entity group version.""" # Prepare several instances of AuthDB to be used in mocks. auth_db_v0 = api.AuthDB(entity_group_version=0) auth_db_v0_again = api.AuthDB(entity_group_version=0) # Fetch initial copy of AuthDB. self.set_time(0) self.set_fetched_auth_db(auth_db_v0) self.assertEqual(auth_db_v0, api.get_process_auth_db()) # Make cache expire, but setup fetch_auth_db to return a new instance of # AuthDB, but with same entity group version. Old known instance of AuthDB # should be reused. self.set_time(api.get_process_cache_expiration_sec() + 1) self.set_fetched_auth_db(auth_db_v0_again) self.assertTrue(api.get_process_auth_db() is auth_db_v0)